diff --git a/example/Jamfile b/example/Jamfile deleted file mode 100644 index 5bc390fc..00000000 --- a/example/Jamfile +++ /dev/null @@ -1,28 +0,0 @@ -# Boost.Format Library Example Jamfile -# -# Copyright (c) 2003 Samuel Krempp -# -# Use, modification, and distribution are subject to the -# Boost Software License, Version 1.0. (See accompanying file -# LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -project libs/format/example ; - -exe sample_formats - : sample_formats.cpp - ; - -exe sample_advanced - : sample_advanced.cpp - ; - -exe sample_new_features - : sample_new_features.cpp - : clang:-Wno-invalid-source-encoding - ; - -exe sample_userType - : sample_userType.cpp - ; - - diff --git a/example/Jamfile.v2 b/example/Jamfile.v2 new file mode 100644 index 00000000..88db0e79 --- /dev/null +++ b/example/Jamfile.v2 @@ -0,0 +1,21 @@ +# Boost.Format Library example Jamfile +# +# Copyright (c) 2003 Samuel Krempp +# +# Distributed under the Boost Software License, Version 1.0. (See accompany- +# ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +project libs/format/example + : requirements + /boost/test//boost_test_exec_monitor/static + ; + +import testing ; + +test-suite "format-examples" + : [ run sample_advanced.cpp ] + [ run sample_formats.cpp ] + [ run sample_new_features.cpp ] + [ run sample_userType.cpp ] + ; + diff --git a/include/boost/format/parsing.hpp b/include/boost/format/parsing.hpp index e2d0f09f..00074925 100644 --- a/include/boost/format/parsing.hpp +++ b/include/boost/format/parsing.hpp @@ -29,12 +29,12 @@ namespace detail { #if defined(BOOST_NO_STD_LOCALE) // streams will be used for narrow / widen. but these methods are not const template - T& const_or_not(const T& x) { + T& const_or_not(const T& x) { return const_cast (x); } #else template - const T& const_or_not(const T& x) { + const T& const_or_not(const T& x) { return x; } #endif @@ -51,11 +51,11 @@ namespace detail { # else ignore_unused(fac); using namespace std; - return isdigit(c) != 0; -#endif + return isdigit(c) != 0; +#endif } - - template + + template Iter wrap_scan_notdigit(const Facet & fac, Iter beg, Iter end) { using namespace std; for( ; beg!=end && wrap_isdigit(fac, *beg); ++beg) ; @@ -68,8 +68,8 @@ namespace detail { // Effects : read sequence and convert digits into integral n, of type Res // Returns : n template - Iter str2int (const Iter & start, const Iter & last, Res & res, - const Facet& fac) + Iter str2int (const Iter & start, const Iter & last, Res & res, + const Facet& fac) { using namespace std; Iter it; @@ -82,33 +82,16 @@ namespace detail { return it; } - // skip printf's "asterisk-fields" directives in the format-string buf - // Input : char string, with starting index *pos_p - // a Facet merely to use its widen/narrow member function - // Effects : advance *pos_p by skipping printf's asterisk fields. - // Returns : nothing - template - Iter skip_asterisk(Iter start, Iter last, const Facet& fac) - { - using namespace std; - ++ start; - start = wrap_scan_notdigit(fac, start, last); - if(start!=last && *start== const_or_not(fac).widen( '$') ) - ++start; - return start; - } - - // auxiliary func called by parse_printf_directive // for centralising error handling // it either throws if user sets the corresponding flag, or does nothing. - inline void maybe_throw_exception(unsigned char exceptions, + inline void maybe_throw_exception(unsigned char exceptions, std::size_t pos, std::size_t size) { if(exceptions & io::bad_format_string_bit) boost::throw_exception(io::bad_format_string(pos, size) ); } - + // Input: the position of a printf-directive in the format-string // a basic_ios& merely to use its widen/narrow member function @@ -121,7 +104,7 @@ namespace detail { // this directive // *fpar is set with the parameters read in the directive template - bool parse_printf_directive(Iter & start, const Iter& last, + bool parse_printf_directive(Iter & start, const Iter& last, detail::format_item * fpar, const Facet& fac, std::size_t offset, unsigned char exceptions) @@ -138,8 +121,8 @@ namespace detail { if(start>= last) { // empty directive : this is a trailing % maybe_throw_exception(exceptions, start-start0 + offset, fstring_size); return false; - } - + } + if(*start== const_or_not(fac).widen( '|')) { in_brackets=true; if( ++start >= last ) { @@ -149,7 +132,7 @@ namespace detail { } // the flag '0' would be picked as a digit for argument order, but here it's a flag : - if(*start== const_or_not(fac).widen( '0')) + if(*start== const_or_not(fac).widen( '0')) goto parse_flags; // handle argument order (%2$d) or possibly width specification: %2d @@ -160,22 +143,20 @@ namespace detail { maybe_throw_exception(exceptions, start-start0+offset, fstring_size); return false; } - + // %N% case : this is already the end of the directive if( *start == const_or_not(fac).widen( '%') ) { fpar->argN_ = n-1; ++start; - if( in_brackets) - maybe_throw_exception(exceptions, start-start0+offset, fstring_size); - // but don't return. maybe "%" was used in lieu of '$', so we go on. - else - return true; + if( in_brackets) + maybe_throw_exception(exceptions, start-start0+offset, fstring_size); + return true; } if ( *start== const_or_not(fac).widen( '$') ) { fpar->argN_ = n-1; ++start; - } + } else { // non-positional directive fpar->fmtstate_.width_ = n; @@ -183,12 +164,12 @@ namespace detail { goto parse_precision; } } - - parse_flags: + + parse_flags: // handle flags while (start != last) { // as long as char is one of + - = _ # 0 or ' ' switch ( wrap_narrow(fac, *start, 0)) { - case '\'': + case '\'': break; // no effect yet. (painful to implement) case '-': fpar->fmtstate_.flags_ |= std::ios_base::left; @@ -221,18 +202,18 @@ namespace detail { if( start>=last) { maybe_throw_exception(exceptions, start-start0+offset, fstring_size); - return true; + return true; } + // first skip 'asterisk fields' : * or num (length) parse_width: - // first skip 'asterisk fields' : *, or *N$ if(*start == const_or_not(fac).widen( '*') ) - start = skip_asterisk(start, last, fac); - if(start!=last && wrap_isdigit(fac, *start)) + ++start; + else if(start!=last && wrap_isdigit(fac, *start)) start = str2int(start, last, fpar->fmtstate_.width_, fac); parse_precision: - if( start>= last) { + if( start>= last) { maybe_throw_exception(exceptions, start-start0+offset, fstring_size); return true; } @@ -240,29 +221,31 @@ namespace detail { if (*start== const_or_not(fac).widen( '.')) { ++start; if(start != last && *start == const_or_not(fac).widen( '*') ) - start = skip_asterisk(start, last, fac); - if(start != last && wrap_isdigit(fac, *start)) { + ++start; + else if(start != last && wrap_isdigit(fac, *start)) { start = str2int(start, last, fpar->fmtstate_.precision_, fac); precision_set = true; } else fpar->fmtstate_.precision_ =0; } - + // argument type modifiers - while (start != last) { // as long as char is one of 'h', 'l', 'j', 'z', or 'L' + while (start != last) { switch (wrap_narrow(fac, *start, 0)) { case 'h': case 'l': case 'j': case 'z': case 'L': - // currently boost::format ignores argument type modifiers as it relies on + // boost::format ignores argument type modifiers as it relies on // the type of the argument fed into it by operator % - // also note that the ptrdiff_t argument type 't' from C++11 is not honored - // because it was already in use as the tabulation specifier here break; + // Note that the ptrdiff_t argument type 't' from C++11 is not honored + // because it was already in use as the tabulation specifier in boost::format + // case 't': + // Microsoft extensions: // https://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx @@ -306,7 +289,7 @@ namespace detail { goto parse_conversion_specification; } ++start; - } // loop on argument type modifiers to pick up 'hh', 'll' + } // loop on argument type modifiers to pick up 'hh', 'll', and the more complex microsoft ones parse_conversion_specification: if (start >= last || mssiz) { @@ -366,7 +349,7 @@ namespace detail { BOOST_FALLTHROUGH; case 'e': fpar->fmtstate_.flags_ |= std::ios_base::scientific; - break; + break; case 'F': fpar->fmtstate_.flags_ |= std::ios_base::uppercase; BOOST_FALLTHROUGH; @@ -383,39 +366,41 @@ namespace detail { // Tabulation (a boost::format extension) case 'T': ++start; - if( start >= last) + if( start >= last) { maybe_throw_exception(exceptions, start-start0+offset, fstring_size); - else + return false; + } else { fpar->fmtstate_.fill_ = *start; + } fpar->pad_scheme_ |= format_item_t::tabulation; - fpar->argN_ = format_item_t::argN_tabulation; + fpar->argN_ = format_item_t::argN_tabulation; break; - case 't': + case 't': fpar->fmtstate_.fill_ = const_or_not(fac).widen( ' '); fpar->pad_scheme_ |= format_item_t::tabulation; - fpar->argN_ = format_item_t::argN_tabulation; + fpar->argN_ = format_item_t::argN_tabulation; break; // Character case 'C': - case 'c': + case 'c': fpar->truncate_ = 1; break; // String case 'S': - case 's': + case 's': if(precision_set) // handle truncation manually, with own parameter. fpar->truncate_ = fpar->fmtstate_.precision_; fpar->fmtstate_.precision_ = 6; // default stream precision. break; // %n is insecure and ignored by boost::format - case 'n' : + case 'n' : fpar->argN_ = format_item_t::argN_ignored; break; - default: + default: maybe_throw_exception(exceptions, start-start0+offset, fstring_size); } ++start; @@ -432,10 +417,10 @@ namespace detail { // -end parse_printf_directive() template - int upper_bound_from_fstring(const String& buf, + int upper_bound_from_fstring(const String& buf, const typename String::value_type arg_mark, - const Facet& fac, - unsigned char exceptions) + const Facet& fac, + unsigned char exceptions) { // quick-parsing of the format-string to count arguments mark (arg_mark, '%') // returns : upper bound on the number of format items in the format strings @@ -452,7 +437,7 @@ namespace detail { } } if(buf[i1+1] == buf[i1] ) {// escaped "%%" - i1+=2; continue; + i1+=2; continue; } ++i1; @@ -465,8 +450,8 @@ namespace detail { return num_items; } template inline - void append_string(String& dst, const String& src, - const typename String::size_type beg, + void append_string(String& dst, const String& src, + const typename String::size_type beg, const typename String::size_type end) { dst.append(src.begin()+beg, src.begin()+end); } @@ -480,19 +465,19 @@ namespace detail { // format :: parse(..) template - basic_format& basic_format:: + basic_format& basic_format:: parse (const string_type& buf) { - // parse the format-string + // parse the format-string using namespace std; #if !defined(BOOST_NO_STD_LOCALE) const std::ctype & fac = BOOST_USE_FACET( std::ctype, getloc()); #else - io::basic_oaltstringstream fac; + io::basic_oaltstringstream fac; //has widen and narrow even on compilers without locale #endif const Ch arg_mark = io::detail::const_or_not(fac).widen( '%'); - bool ordered_args=true; + bool ordered_args=true; int max_argN=-1; // A: find upper_bound on num_items and allocates arrays @@ -507,10 +492,10 @@ namespace detail { int cur_item=0; while( (i1=buf.find(arg_mark,i1)) != string_type::npos ) { string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_; - if( buf[i1+1] == buf[i1] ) { // escaped mark, '%%' + if( buf[i1+1] == buf[i1] ) { // escaped mark, '%%' io::detail::append_string(piece, buf, i0, i1+1); i1+=2; i0=i1; - continue; + continue; } BOOST_ASSERT( static_cast(cur_item) < items_.size() || cur_item==0); @@ -524,7 +509,7 @@ namespace detail { it, buf.end(), &items_[cur_item], fac, i1, exceptions()); i1 = it - buf.begin(); if( ! parse_ok ) // the directive will be printed verbatim - continue; + continue; i0=i1; items_[cur_item].compute_states(); // process complex options, like zeropad, into params @@ -539,13 +524,13 @@ namespace detail { ++cur_item; } // loop on %'s BOOST_ASSERT(cur_item == num_items); - + // store the final piece of string { string_type & piece = (cur_item==0) ? prefix_ : items_[cur_item-1].appendix_; io::detail::append_string(piece, buf, i0, buf.size()); } - + if( !ordered_args) { if(max_argN >= 0 ) { // dont mix positional with non-positionnal directives if(exceptions() & io::bad_format_string_bit) @@ -562,7 +547,7 @@ namespace detail { } max_argN = non_ordered_items-1; } - + // C: set some member data : items_.resize(num_items, format_item_t(io::detail::const_or_not(fac).widen( ' ')) ); diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index b256528e..ba442ba7 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -5,8 +5,8 @@ # Distributed under the Boost Software License, Version 1.0. (See accompany- # ing file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -project - : requirements +project libs/format/test + : requirements /boost/test//boost_test_exec_monitor/static ; diff --git a/test/format_test2.cpp b/test/format_test2.cpp index 3ac47961..f7a6ea65 100644 --- a/test/format_test2.cpp +++ b/test/format_test2.cpp @@ -15,14 +15,14 @@ #include #include -#include +#include #include #if !defined(BOOST_NO_STD_LOCALE) #include #endif -#define BOOST_INCLUDE_MAIN +#define BOOST_INCLUDE_MAIN #include @@ -55,7 +55,7 @@ int test_main(int, char* []) const Rational cr(9,16); string s; - s = str(format("%5%. %5$=6s . %1% format %5%, c'%3% %1% %2%.\n") + s = str(format("%5%. %5$=6s . %1% format %5%, c'%3% %1% %2%.\n") % "le" % "bonheur" % "est" % "trop" % group(setfill('_'), "bref") ); if(s != "bref. _bref_ . le format bref, c'est le bonheur.\n") { @@ -66,19 +66,19 @@ int test_main(int, char* []) s = str(format("%+8d %-8d\n") % r % cr ); if(s != " +16/+9 9/16 \n") { - cerr << s; + cerr << s; BOOST_ERROR("(user-type) formatting result incorrect"); } s = str(format("[%0+4d %0+8d %-08d]\n") % 8 % r % r); if(s != "[+008 +0016/+9 16/9 ]\n") { - cerr << s; + cerr << s; BOOST_ERROR("(zero-padded user-type) formatting result incorrect"); } s = str( format("%1%, %20T_ (%|2$5|,%|3$5|)\n") % "98765" % 1326 % 88 ) ; - if( s != "98765, _____________ ( 1326, 88)\n" ) + if( s != "98765, _____________ ( 1326, 88)\n" ) BOOST_ERROR("(tabulation) formatting result incorrect"); s = str( format("%s, %|20t|=") % 88 ) ; if( s != "88, =" ) { @@ -89,7 +89,7 @@ int test_main(int, char* []) s = str(format("%.2s %8c.\n") % "root" % "user" ); if(s != "ro u.\n") { - cerr << s; + cerr << s; BOOST_ERROR("(truncation) formatting result incorrect"); } @@ -112,7 +112,7 @@ int test_main(int, char* []) // flags in the format string are not sticky // and hex in argument overrrides type-char d (->decimal) : - s = str( format("%2$#4d %|1$4| %|2$#4| %|3$|") + s = str( format("%2$#4d %|1$4| %|2$#4| %|3$|") % 101 % group(setfill('_'), hex, 2) % 103 ); @@ -121,7 +121,7 @@ int test_main(int, char* []) - // flag '0' is tricky . + // flag '0' is tricky . // left-align cancels '0': s = str( format("%2$0#12X %2$0#-12d %1$0#10d \n") % -20 % 10 ); if( s != "0X000000000A 10 -000000020 \n"){ @@ -129,11 +129,11 @@ int test_main(int, char* []) BOOST_ERROR("formatting error. (flag 0)"); } - // actually testing floating point output is implementation + // actually testing floating point output is implementation // specific so we're just going to do minimal checking... double dbl = 1234567.890123f; -#if __cplusplus >= 201103L || BOOST_VERSION_NUMBER_MAJOR(BOOST_COMP_MSVC) >= 12 +#if (__cplusplus >= 201103L) || (BOOST_VERSION_NUMBER_MAJOR(BOOST_COMP_MSVC) >= 12) // msvc-12.0 and later have support for hexfloat but do not set __cplusplus to a C++11 value BOOST_CHECK(boost::starts_with((boost::format("%A") % dbl).str(), "0X")); BOOST_CHECK(boost::starts_with((boost::format("%a") % dbl).str(), "0x")); @@ -179,12 +179,6 @@ int test_main(int, char* []) BOOST_CHECK_EQUAL((boost::format("%Id") % 123).str(), "123"); BOOST_CHECK_EQUAL((boost::format("%I32d") % 456).str(), "456"); BOOST_CHECK_EQUAL((boost::format("%I64d") % 789).str(), "789"); - BOOST_CHECK_THROW(boost::format("%I2d"), boost::io::bad_format_string); - BOOST_CHECK_THROW(boost::format("%I3d"), boost::io::bad_format_string); - BOOST_CHECK_THROW(boost::format("%I33d"), boost::io::bad_format_string); - BOOST_CHECK_THROW(boost::format("%I4d"), boost::io::bad_format_string); - BOOST_CHECK_THROW(boost::format("%I63d"), boost::io::bad_format_string); - BOOST_CHECK_THROW(boost::format("%I128d"), boost::io::bad_format_string); // issue-36 volatile (and const) keyword volatile int vint = 1234567; @@ -192,5 +186,30 @@ int test_main(int, char* []) volatile const int vcint = 7654321; BOOST_CHECK_EQUAL((boost::format("%1%") % vcint).str(), "7654321"); + // skip width if '*' + BOOST_CHECK_EQUAL((boost::format("%*d") % vint).str(), "1234567"); + + // internal ios flag + BOOST_CHECK_EQUAL((boost::format("%_6d") % -77).str(), "- 77"); + + // combining some flags + BOOST_CHECK_EQUAL((boost::format("%+05.5d" ) % 77).str(), "+0077"); + BOOST_CHECK_EQUAL((boost::format("%+ 5.5d" ) % 77).str(), " +77"); + BOOST_CHECK_EQUAL((boost::format("%+_ 5.5d" ) % 77).str(), "+ 77"); + BOOST_CHECK_EQUAL((boost::format("%+- 5.5d" ) % 77).str(), "+77 "); + BOOST_CHECK_EQUAL((boost::format("%+05.5d" ) % -77).str(), "-0077"); + BOOST_CHECK_EQUAL((boost::format("%+ 5.5d" ) % -77).str(), " -77"); + BOOST_CHECK_EQUAL((boost::format("%+_ 5.5d" ) % -77).str(), "- 77"); + BOOST_CHECK_EQUAL((boost::format("%+- 5.5d" ) % -77).str(), "-77 "); + + // reuse state and reset format flags + std::string mystr("abcdefghijklmnop"); + BOOST_CHECK_EQUAL((boost::format("%2.2s %-4.4s % 8.8s") + % mystr % mystr % mystr).str(), "ab abcd abcdefg"); + + // coverage + format fmt("%1%%2%%3%"); + fmt = fmt; + return 0; } diff --git a/test/format_test3.cpp b/test/format_test3.cpp index 16336271..dd85d36e 100644 --- a/test/format_test3.cpp +++ b/test/format_test3.cpp @@ -13,10 +13,10 @@ #define BOOST_FORMAT_STATIC_STREAM #include "boost/format.hpp" -#include +#include #include -#define BOOST_INCLUDE_MAIN +#define BOOST_INCLUDE_MAIN #include struct Rational { @@ -37,8 +37,8 @@ int test_main(int, char* []) using boost::str; string s, s2; - // special paddings - s = str( format("[%=6s] [%+6s] [%+6s] [% 6s] [%+6s]\n") + // special paddings + s = str( format("[%=6s] [%+6s] [%+6s] [% 6s] [%+6s]\n") % 123 % group(internal, setfill('W'), 234) % group(internal, setfill('X'), -345) @@ -50,7 +50,7 @@ int test_main(int, char* []) BOOST_ERROR("formatting error. (with special paddings)"); } - s = str( format("[% 6.8s] [% 8.6s] [% 7.7s]\n") + s = str( format("[% 6.8s] [% 8.6s] [% 7.7s]\n") % group(internal, setfill('x'), Rational(12345,54321)) % group(internal, setfill('x'), Rational(123,45)) % group(internal, setfill('x'), Rational(123,321)) @@ -60,12 +60,12 @@ int test_main(int, char* []) BOOST_ERROR("formatting error. (with special paddings)"); } - s = str( format("[% 6.8s] [% 6.8s] [% 6.8s] [% 6.8s] [%6.8s]\n") + s = str( format("[% 6.8s] [% 6.8s] [% 6.8s] [% 6.8s] [%6.8s]\n") % 1234567897 % group(setfill('x'), 12) % group(internal, setfill('x'), 12) % group(internal, setfill('x'), 1234567890) - % group(internal, setfill('x'), 123456) + % group(internal, setfill('x'), 123456) ); if(s != (s2="[ 1234567] [xxx 12] [ xxx12] [ 1234567] [123456]\n") ) { cerr << s << s2; @@ -77,7 +77,7 @@ int test_main(int, char* []) % group(setfill('x'), 12) % group(internal, setfill('x'), 12) % group(internal, setfill('x'), 1234567890) - % group(internal, setfill('x'), 12345) + % group(internal, setfill('x'), 12345) ); if(s != (s2="[ 12345] [xxx 12] [ xxx12] [ xx12345] [ xx12345]\n") ) { cerr << s << s2; @@ -110,7 +110,7 @@ int test_main(int, char* []) BOOST_CHECK_EQUAL(bf.remaining_args(), 1); BOOST_CHECK_EQUAL(bf.cur_arg(), 4); } - // testcase for bug reported at + // testcase for bug reported at // http://lists.boost.org/boost-users/2006/05/19723.php { format f("%40t%1%"); @@ -122,11 +122,11 @@ int test_main(int, char* []) // testcase for bug reported at // http://lists.boost.org/boost-users/2005/11/15454.php std::string l_param; - std::string l_str = (boost::format("here is an empty string: %1%") % l_param).str(); + std::string l_str = (boost::format("here is an empty string: %1%") % l_param).str(); BOOST_CHECK_EQUAL(std::string("here is an empty string: "), l_str); // testcase for SourceForge bug #1506914 - std::string arg; // empty string + std::string arg; // empty string s = str(format("%=8s") % arg); BOOST_CHECK_EQUAL(std::string(" "), s); diff --git a/test/format_test_exceptions.cpp b/test/format_test_exceptions.cpp index dd2b4208..e79ddaeb 100644 --- a/test/format_test_exceptions.cpp +++ b/test/format_test_exceptions.cpp @@ -13,19 +13,96 @@ #define BOOST_FORMAT_STATIC_STREAM #include "boost/format.hpp" -#include +#include +#include #include -#define BOOST_INCLUDE_MAIN +#define BOOST_INCLUDE_MAIN #include +#define CHECK_INVALID_0(FMT, EX) { BOOST_CHECK_THROW((boost::format(FMT)).str(), EX); \ + boost::format safe; safe.exceptions(boost::io::no_error_bits); BOOST_CHECK_NO_THROW(safe.parse(FMT)); BOOST_CHECK_NO_THROW((safe).str()); } +#define CHECK_INVALID_1(FMT, _1, EX) { BOOST_CHECK_THROW((boost::format(FMT) % _1).str(), EX); \ + boost::format safe; safe.exceptions(boost::io::no_error_bits); BOOST_CHECK_NO_THROW(safe.parse(FMT)); BOOST_CHECK_NO_THROW((safe % _1).str()); } +#define CHECK_INVALID_2(FMT, _1, _2, EX) { BOOST_CHECK_THROW((boost::format(FMT) % _1 % _2).str(), EX); \ + boost::format safe; safe.exceptions(boost::io::no_error_bits); BOOST_CHECK_NO_THROW(safe.parse(FMT)); BOOST_CHECK_NO_THROW((safe % _1 % _2).str()); } + int test_main(int, char* []) { + using namespace boost::io; + // https://svn.boost.org/trac10/ticket/8735 - BOOST_CHECK_THROW((boost::format("%0%") % 1), boost::io::bad_format_string); - BOOST_CHECK_THROW((boost::format("%1%") % 1 % 2), boost::io::too_many_args); - BOOST_CHECK_THROW((boost::format("%1% %2%") % 1).str(), boost::io::too_few_args); + CHECK_INVALID_0("%", bad_format_string); // no conversion specifier + CHECK_INVALID_0("%|", bad_format_string); // truncated + CHECK_INVALID_0("%|%", bad_format_string); // mismatched bars + CHECK_INVALID_0("%|2%", bad_format_string); // mismatched bars + CHECK_INVALID_0("%'", bad_format_string); // flag ' is ignored, no conversion specifier + CHECK_INVALID_0("%2", bad_format_string); // no terminating percent + CHECK_INVALID_0("%*", bad_format_string); // truncated + CHECK_INVALID_1("%$2", 1, bad_format_string); // no conversion specifier + CHECK_INVALID_1("%$2.*", 1, bad_format_string); // no conversion specifier + CHECK_INVALID_0("%0%", bad_format_string); // positional arguments start at 1 + CHECK_INVALID_2("%1%", 1, 2, too_many_args); + CHECK_INVALID_1("%1% %2%", 1, too_few_args); + + CHECK_INVALID_0("%I", bad_format_string); + CHECK_INVALID_0("%I2", bad_format_string); + CHECK_INVALID_0("%I2d", bad_format_string); + CHECK_INVALID_0("%I3", bad_format_string); + CHECK_INVALID_0("%I3d", bad_format_string); + CHECK_INVALID_0("%I32", bad_format_string); + CHECK_INVALID_0("%I33", bad_format_string); + CHECK_INVALID_0("%I6", bad_format_string); + CHECK_INVALID_0("%I62", bad_format_string); + CHECK_INVALID_0("%I63", bad_format_string); + CHECK_INVALID_0("%I63d", bad_format_string); + CHECK_INVALID_0("%I64", bad_format_string); + CHECK_INVALID_0("%I128d", bad_format_string); + CHECK_INVALID_0("%3.*4d", bad_format_string); + + // mixing positional and non-positional + CHECK_INVALID_2("%1% %2d", 1, 2, bad_format_string); + CHECK_INVALID_2("%2d %2%", 1, 2, bad_format_string); + + // found while improving coverage - a number following the * for width + // or precision was being eaten instead of being treated as an error + CHECK_INVALID_0("%1$*4d", bad_format_string); + CHECK_INVALID_0("%1$05.*32d", bad_format_string); + CHECK_INVALID_0("%1$05.*6", bad_format_string); + + // found while improving coverage, this caused an unhandled exception + // the "T" conversion specifier didn't handle the exception flags properly + CHECK_INVALID_0("%1$T", bad_format_string); // missing fill character + + // test what() on exceptions + format_error base_err; + BOOST_CHECK_GT(strlen(base_err.what()), 0u); + + bad_format_string bfs(2, 3); + BOOST_CHECK(boost::contains(bfs.what(), "bad_format_string")); + + too_few_args tfa(5, 4); + BOOST_CHECK(boost::contains(tfa.what(), "format-string")); + + too_many_args tma(4, 5); + BOOST_CHECK(boost::contains(tma.what(), "format-string")); + + boost::io::out_of_range oor(1, 2, 3); + BOOST_CHECK(boost::contains(oor.what(), "out_of_range")); + + // bind and unbind + boost::format two("%1%, %2%"); + two.bind_arg(1, 1); + two.bind_arg(2, 2); + BOOST_CHECK_EQUAL(two.str(), "1, 2"); + + BOOST_CHECK_NO_THROW(two.clear_bind(1)); + BOOST_CHECK_THROW(two.str(), too_few_args); + BOOST_CHECK_THROW(two.clear_bind(1), boost::io::out_of_range); + two.exceptions(no_error_bits); + BOOST_CHECK_NO_THROW(two.clear_bind(1)); + BOOST_CHECK_NO_THROW(two.str()); return 0; }