Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add support for parsing (and ignoring) argument types 'j', 'z' #42

Merged
merged 1 commit into from Oct 24, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
58 changes: 34 additions & 24 deletions doc/format.html
Expand Up @@ -339,24 +339,21 @@ <h3>printf format specifications</h3>
Supplying more, or less, than <i>P</i> arguments raises an exception.
(unless it was set otherwise, see <a href="#exceptions">exceptions</a>)</p>

<p><br>
<br>
A specification <i>spec</i> has the form : [ <i>N</i><b>$</b> ] [
<i>flags</i> ] [ <i>width</i> ] [ <b>.</b> <i>precision</i> ]
<i>type-char</i><br>
<br>
Fields insided square brackets are optional. Each of those fields are
explained one by one in the following list :</p>
<p>A specification <i>spec</i> has the form:
<pre> [ <i>N</i><b>$</b> ] [ <i>flags</i> ] [ <i>width</i> ] [ <b>.</b> <i>precision</i> ] [ <i>argument-type</i> ] <i>conversion-specifier</i></pre>

Fields inside square brackets are optional. Each of those fields are
explained one by one in the following list:</p>

<ul>
<li><i>N</i> <b>$</b> (optional field) specifies that the format
specification applies to the <i>N</i>-th argument. (it is called a
<i>positional format specification</i>)<br>
specification applies to the <i>N</i>-th argument (it is called a
<i>positional format specification</i>).<br>
If this is not present, arguments are taken one by one. (and it is then
an error to later supply an argument number)</li>
an error to later supply an argument number)</li><br />

<li>
<i>flags</i> is a sequences of any of those :
<i>flags</i> is a sequence of any of these:

<blockquote>
<table border="1" cellpadding="5" summary="">
Expand Down Expand Up @@ -432,7 +429,7 @@ <h3>printf format specifications</h3>
alignment</td>
</tr>
</table>
</blockquote>
</blockquote><br />
</li>

<li><i>width</i> specifies a minimal width for the string resulting form
Expand All @@ -443,7 +440,7 @@ <h3>printf format specifications</h3>
output of <a href="#user-defined">user-defined types</a> (that might call
<i>operator&lt;&lt;</i> many times on several members), the width is
handled after stream conversion of the whole argument object, in the
format class code.</li>
format class code.</li><br />

<li>
<i>precision</i> (preceded by a point), sets the stream's
Expand All @@ -465,18 +462,30 @@ <h3>printf format specifications</h3>
meaning : the conversion string is truncated to the <i>precision</i>
first chars. (Note that the eventual padding to <i>width</i> is done
after truncation.)</li>
</ul>
</ul><br />
</li>

<li>
<i>type-char</i>. it does <b>not</b> impose the concerned argument to
be of a restricted set of types, but merely sets the flags that are
associated with this type specification.
<i>argument-type</i> is used by the printf family to properly process
the arguments being passed in through varargs. With <code>boost::format</code>
the arguments are fed into format through <code>operator %</code> which
allows the template to carry the argument type. Therefore the classical
printf style argument type is consumed and ignored.
Argument-types <code>hh</code>, <code>h</code>, <code>l</code>, <code>ll</code>, <code>j</code>,
<code>z</code>, and <code>L</code> are recognized. Argument-type <code>t</code> from the
standard is not recognized as an argument type, as it has been in use as a
conversion specifier for tabular output for many years by <code>boost::format</code>.
</li><br />

<li>
<i>conversion-specifier</i> does <b>not</b> impose the concerned argument
to be of a restricted set of types, but merely sets the ios flags that are
associated with a type specification:

<blockquote>
<table border="1" cellpadding="5" summary="">
<tr>
<td><b>Type-Char</b></td>
<td><b>conversion-specifier</b></td>

<td><b>Meaning</b></td>

Expand Down Expand Up @@ -579,11 +588,8 @@ <h3>printf format specifications</h3>
</table>
</blockquote>

<p>Note that the 'n' type specification is ignored (and so is the
corresponding argument), because it does not fit in this context.<br>
Also, printf 'l', 'L', or 'h' modifiers (to indicate wide, long or
short types) are supported (and simply have no effect on the internal
stream).</p>
<p>Note that the 'n' conversion-specifier is ignored (and so is the
corresponding argument), because it does not fit in this context.</p>
</li>
</ul><a name="new_directives" id="new_directives"></a>

Expand Down Expand Up @@ -651,6 +657,8 @@ <h2>Differences of behaviour vs printf</h2>Suppose you have variables
<i>printf("%1$d:%2$.*3$d:%4$.*3$d\n", hour, min, precision, sec);</i><br>
This class does not support this mechanism for now. so such precision or
width fields are quietly ignored by the parsing.</li>

<li>argument-type is ignored</li>
</ul>Also, note that the special <b>'n'</b> type-specification (used to
tell printf to save in a variable the number of characters output by the
formatting) has no effect in format.<br>
Expand Down Expand Up @@ -779,6 +787,8 @@ <h2>Alternatives</h2>
Nelson's library</a> was intented as demonstration of alternative
solutions in discussions on Boost's list for the design of
Boost.format.</li>

<li><a href="http://fmtlib.net/latest/index.html">{fmt}</a> by Victor Zverovich.</li>
</ul><a name="exceptions" id="exceptions"></a>
<hr>

Expand Down
90 changes: 51 additions & 39 deletions include/boost/format/parsing.hpp
Expand Up @@ -176,7 +176,7 @@ namespace detail {
++start;
}
else {
// non-positionnal directive
// non-positional directive
fpar->fmtstate_.width_ = n;
fpar->argN_ = format_item_t::argN_no_posit;
goto parse_precision;
Expand All @@ -185,38 +185,35 @@ namespace detail {

parse_flags:
// handle flags
while ( start != last) { // as long as char is one of + - = _ # 0 l h or ' '
// misc switches
while (start != last) { // as long as char is one of + - = _ # 0 or ' '
switch ( wrap_narrow(fac, *start, 0)) {
case '\'' : break; // no effect yet. (painful to implement)
case 'l':
case 'h': // short/long modifier : for printf-comaptibility (no action needed)
break;
case '-':
fpar->fmtstate_.flags_ |= std::ios_base::left;
break;
case '=':
fpar->pad_scheme_ |= format_item_t::centered;
break;
case '_':
fpar->fmtstate_.flags_ |= std::ios_base::internal;
break;
case ' ':
fpar->pad_scheme_ |= format_item_t::spacepad;
break;
case '+':
fpar->fmtstate_.flags_ |= std::ios_base::showpos;
break;
case '0':
fpar->pad_scheme_ |= format_item_t::zeropad;
// need to know alignment before really setting flags,
// so just add 'zeropad' flag for now, it will be processed later.
break;
case '#':
fpar->fmtstate_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
break;
default:
goto parse_width;
case '\'':
break; // no effect yet. (painful to implement)
case '-':
fpar->fmtstate_.flags_ |= std::ios_base::left;
break;
case '=':
fpar->pad_scheme_ |= format_item_t::centered;
break;
case '_':
fpar->fmtstate_.flags_ |= std::ios_base::internal;
break;
case ' ':
fpar->pad_scheme_ |= format_item_t::spacepad;
break;
case '+':
fpar->fmtstate_.flags_ |= std::ios_base::showpos;
break;
case '0':
fpar->pad_scheme_ |= format_item_t::zeropad;
// need to know alignment before really setting flags,
// so just add 'zeropad' flag for now, it will be processed later.
break;
case '#':
fpar->fmtstate_.flags_ |= std::ios_base::showpoint | std::ios_base::showbase;
break;
default:
goto parse_width;
}
++start;
} // loop on flag.
Expand All @@ -225,8 +222,8 @@ namespace detail {
maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
return true;
}

parse_width:
// handle width spec
// first skip 'asterisk fields' : *, or *N$
if(*start == const_or_not(fac).widen( '*') )
start = skip_asterisk(start, last, fac);
Expand All @@ -251,13 +248,28 @@ namespace detail {
fpar->fmtstate_.precision_ =0;
}

// handle formatting-type flags :
while( start != last && ( *start== const_or_not(fac).widen( 'l')
|| *start== const_or_not(fac).widen( 'L')
|| *start== const_or_not(fac).widen( 'h')) )
// argument type modifiers
while (start != last) { // as long as char is one of 'h', 'l', 'j', 'z', or 'L'
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
// 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;
default:
goto parse_conversion_specification;
}
++start;
if( start>=last) {
maybe_throw_exception(exceptions, start-start0+offset, fstring_size);
} // loop on argument type modifiers to pick up 'hh', 'll'

parse_conversion_specification:
if (start >= last) {
maybe_throw_exception(exceptions, start - start0 + offset, fstring_size);
return true;
}

Expand Down
11 changes: 11 additions & 0 deletions test/format_test2.cpp
Expand Up @@ -134,5 +134,16 @@ int test_main(int, char* [])
BOOST_CHECK(!(boost::format("%G") % dbl).str().empty());
BOOST_CHECK(!(boost::format("%g") % dbl).str().empty());

// testing argument type parsing - remember argument types are ignored
// because operator % presents the argument type.
unsigned int value = 456;
BOOST_CHECK_EQUAL((boost::format("%hhu") % value).str(), "456");
BOOST_CHECK_EQUAL((boost::format("%hu") % value).str(), "456");
BOOST_CHECK_EQUAL((boost::format("%lu") % value).str(), "456");
BOOST_CHECK_EQUAL((boost::format("%llu") % value).str(), "456");
BOOST_CHECK_EQUAL((boost::format("%ju") % value).str(), "456");
BOOST_CHECK_EQUAL((boost::format("%zu") % value).str(), "456");
BOOST_CHECK(boost::starts_with((boost::format("%Lf") % value).str(), "456"));

return 0;
}