Merge #17682: util: Update tinyformat to upstream

978b25528c util: Update tinyformat to upstream (Wladimir J. van der Laan)

Pull request description:

  Last update was in 2017.
  Updates tinyformat to upstream commit  c42f/tinyformat@705e3f4e1d.
  Re-apply (and mark) bitcoin core specific changes.

  No changes that affect our use, as far as I can see, but this gets rid of the gcc `-Wimplicit-fallthrough` warnings, at least.

ACKs for top commit:
  MarcoFalke:
    ACK 978b25528c, extracted our patches based on the last update, did the update to v2.3.0 myself and re-applied the patches. Only diff is NULL/nullptr and explicit 🔝

Tree-SHA512: 2ba09e1095878d088520f379d545b40c7286ef199ecbbc17fdd5c85bca447d9b0c7a1829d4038bb6d432cd1ff92ad7bba75c0f2f96c71aeb6fa6031002f1ea1d
This commit is contained in:
MarcoFalke 2019-12-09 14:36:15 -05:00
commit 5bf1909dd9
No known key found for this signature in database
GPG key ID: CE2B75697E69A548

View file

@ -33,6 +33,7 @@
// //
// * Type safety and extensibility for user defined types. // * Type safety and extensibility for user defined types.
// * C99 printf() compatibility, to the extent possible using std::ostream // * C99 printf() compatibility, to the extent possible using std::ostream
// * POSIX extension for positional arguments
// * Simplicity and minimalism. A single header file to include and distribute // * Simplicity and minimalism. A single header file to include and distribute
// with your projects. // with your projects.
// * Augment rather than replace the standard stream formatting mechanism // * Augment rather than replace the standard stream formatting mechanism
@ -42,7 +43,7 @@
// Main interface example usage // Main interface example usage
// ---------------------------- // ----------------------------
// //
// To print a date to std::cout: // To print a date to std::cout for American usage:
// //
// std::string weekday = "Wednesday"; // std::string weekday = "Wednesday";
// const char* month = "July"; // const char* month = "July";
@ -52,6 +53,14 @@
// //
// tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min); // tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
// //
// POSIX extension for positional arguments is available.
// The ability to rearrange formatting arguments is an important feature
// for localization because the word order may vary in different languages.
//
// Previous example for German usage. Arguments are reordered:
//
// tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min);
//
// The strange types here emphasize the type safety of the interface; it is // The strange types here emphasize the type safety of the interface; it is
// possible to print a std::string using the "%s" conversion, and a // possible to print a std::string using the "%s" conversion, and a
// size_t using the "%d" conversion. A similar result could be achieved // size_t using the "%d" conversion. A similar result could be achieved
@ -133,12 +142,17 @@ namespace tfm = tinyformat;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Implementation details. // Implementation details.
#include <algorithm> #include <algorithm>
#include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept> // Added for Bitcoin Core
#ifndef TINYFORMAT_ASSERT
# include <cassert>
# define TINYFORMAT_ASSERT(cond) assert(cond)
#endif
#ifndef TINYFORMAT_ERROR #ifndef TINYFORMAT_ERROR
# include <cassert>
# define TINYFORMAT_ERROR(reason) assert(0 && reason) # define TINYFORMAT_ERROR(reason) assert(0 && reason)
#endif #endif
@ -149,13 +163,13 @@ namespace tfm = tinyformat;
#endif #endif
#if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201 #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
// std::showpos is broken on old libstdc++ as provided with OSX. See // std::showpos is broken on old libstdc++ as provided with macOS. See
// http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html // http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
# define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
#endif #endif
#ifdef __APPLE__ #ifdef __APPLE__
// Workaround OSX linker warning: Xcode uses different default symbol // Workaround macOS linker warning: Xcode uses different default symbol
// visibilities for static libs vs executables (see issue #25) // visibilities for static libs vs executables (see issue #25)
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
#else #else
@ -164,6 +178,7 @@ namespace tfm = tinyformat;
namespace tinyformat { namespace tinyformat {
// Added for Bitcoin Core
class format_error: public std::runtime_error class format_error: public std::runtime_error
{ {
public: public:
@ -218,7 +233,7 @@ template<int n> struct is_wchar<wchar_t[n]> {};
template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value> template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
struct formatValueAsType struct formatValueAsType
{ {
static void invoke(std::ostream& /*out*/, const T& /*value*/) { assert(0); } static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); }
}; };
// Specialized version for types that can actually be converted to fmtT, as // Specialized version for types that can actually be converted to fmtT, as
// indicated by the "convertible" template parameter. // indicated by the "convertible" template parameter.
@ -240,8 +255,7 @@ struct formatZeroIntegerWorkaround<T,true>
{ {
static bool invoke(std::ostream& out, const T& value) static bool invoke(std::ostream& out, const T& value)
{ {
if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) {
{
out << "+0"; out << "+0";
return true; return true;
} }
@ -335,8 +349,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
#ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
#endif #endif
else if(ntrunc >= 0) else if (ntrunc >= 0) {
{
// Take care not to overread C strings in truncating conversions like // Take care not to overread C strings in truncating conversions like
// "%.4s" where at most 4 characters may be read. // "%.4s" where at most 4 characters may be read.
detail::formatTruncated(out, value, ntrunc); detail::formatTruncated(out, value, ntrunc);
@ -351,8 +364,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
const char* fmtEnd, int /**/, charType value) \ const char* fmtEnd, int /**/, charType value) \
{ \ { \
switch(*(fmtEnd-1)) \ switch (*(fmtEnd-1)) { \
{ \
case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \
out << static_cast<int>(value); break; \ out << static_cast<int>(value); break; \
default: \ default: \
@ -490,19 +502,19 @@ namespace detail {
// Type-opaque holder for an argument to format(), with associated actions on // Type-opaque holder for an argument to format(), with associated actions on
// the type held as explicit function pointers. This allows FormatArg's for // the type held as explicit function pointers. This allows FormatArg's for
// each argument to be allocated as a homogenous array inside FormatList // each argument to be allocated as a homogeneous array inside FormatList
// whereas a naive implementation based on inheritance does not. // whereas a naive implementation based on inheritance does not.
class FormatArg class FormatArg
{ {
public: public:
FormatArg() FormatArg()
: m_value(nullptr), : m_value(NULL),
m_formatImpl(nullptr), m_formatImpl(NULL),
m_toIntImpl(nullptr) m_toIntImpl(NULL)
{ } { }
template<typename T> template<typename T>
explicit FormatArg(const T& value) FormatArg(const T& value)
: m_value(static_cast<const void*>(&value)), : m_value(static_cast<const void*>(&value)),
m_formatImpl(&formatImpl<T>), m_formatImpl(&formatImpl<T>),
m_toIntImpl(&toIntImpl<T>) m_toIntImpl(&toIntImpl<T>)
@ -511,15 +523,15 @@ class FormatArg
void format(std::ostream& out, const char* fmtBegin, void format(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc) const const char* fmtEnd, int ntrunc) const
{ {
assert(m_value); TINYFORMAT_ASSERT(m_value);
assert(m_formatImpl); TINYFORMAT_ASSERT(m_formatImpl);
m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
} }
int toInt() const int toInt() const
{ {
assert(m_value); TINYFORMAT_ASSERT(m_value);
assert(m_toIntImpl); TINYFORMAT_ASSERT(m_toIntImpl);
return m_toIntImpl(m_value); return m_toIntImpl(m_value);
} }
@ -554,31 +566,63 @@ inline int parseIntAndAdvance(const char*& c)
return i; return i;
} }
// Print literal part of format string and return next format spec // Parse width or precision `n` from format string pointer `c`, and advance it
// position. // to the next character. If an indirection is requested with `*`, the argument
// is read from `args[argIndex]` and `argIndex` is incremented (or read
// from `args[n]` in positional mode). Returns true if one or more
// characters were read.
inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode,
const detail::FormatArg* args,
int& argIndex, int numArgs)
{
if (*c >= '0' && *c <= '9') {
n = parseIntAndAdvance(c);
}
else if (*c == '*') {
++c;
n = 0;
if (positionalMode) {
int pos = parseIntAndAdvance(c) - 1;
if (*c != '$')
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
if (pos >= 0 && pos < numArgs)
n = args[pos].toInt();
else
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
++c;
}
else {
if (argIndex < numArgs)
n = args[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision");
}
}
else {
return false;
}
return true;
}
// Print literal part of format string and return next format spec position.
// //
// Skips over any occurrences of '%%', printing a literal '%' to the // Skips over any occurrences of '%%', printing a literal '%' to the output.
// output. The position of the first % character of the next // The position of the first % character of the next nontrivial format spec is
// nontrivial format spec is returned, or the end of string. // returned, or the end of string.
inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
{ {
const char* c = fmt; const char* c = fmt;
for(;; ++c) for (;; ++c) {
{ if (*c == '\0') {
switch(*c)
{
case '\0':
out.write(fmt, c - fmt); out.write(fmt, c - fmt);
return c; return c;
case '%': }
else if (*c == '%') {
out.write(fmt, c - fmt); out.write(fmt, c - fmt);
if (*(c+1) != '%') if (*(c+1) != '%')
return c; return c;
// for "%%", tack trailing % onto next literal section. // for "%%", tack trailing % onto next literal section.
fmt = ++c; fmt = ++c;
break;
default:
break;
} }
} }
} }
@ -587,23 +631,43 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
// Parse a format string and set the stream state accordingly. // Parse a format string and set the stream state accordingly.
// //
// The format mini-language recognized here is meant to be the one from C99, // The format mini-language recognized here is meant to be the one from C99,
// with the form "%[flags][width][.precision][length]type". // with the form "%[flags][width][.precision][length]type" with POSIX
// positional arguments extension.
//
// POSIX positional arguments extension:
// Conversions can be applied to the nth argument after the format in
// the argument list, rather than to the next unused argument. In this case,
// the conversion specifier character % (see below) is replaced by the sequence
// "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}],
// giving the position of the argument in the argument list. This feature
// provides for the definition of format strings that select arguments
// in an order appropriate to specific languages.
//
// The format can contain either numbered argument conversion specifications
// (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications
// (that is, % and * ), but not both. The only exception to this is that %%
// can be mixed with the "%n$" form. The results of mixing numbered and
// unnumbered argument specifications in a format string are undefined.
// When numbered argument specifications are used, specifying the Nth argument
// requires that all the leading arguments, from the first to the (N-1)th,
// are specified in the format string.
//
// In format strings containing the "%n$" form of conversion specification,
// numbered arguments in the argument list can be referenced from the format
// string as many times as required.
// //
// Formatting options which can't be natively represented using the ostream // Formatting options which can't be natively represented using the ostream
// state are returned in spacePadPositive (for space padded positive numbers) // state are returned in spacePadPositive (for space padded positive numbers)
// and ntrunc (for truncating conversions). argIndex is incremented if // and ntrunc (for truncating conversions). argIndex is incremented if
// necessary to pull out variable width and precision. The function returns a // necessary to pull out variable width and precision. The function returns a
// pointer to the character after the end of the current format spec. // pointer to the character after the end of the current format spec.
inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive, inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode,
bool& spacePadPositive,
int& ntrunc, const char* fmtStart, int& ntrunc, const char* fmtStart,
const detail::FormatArg* formatters, const detail::FormatArg* args,
int& argIndex, int numFormatters) int& argIndex, int numArgs)
{ {
if(*fmtStart != '%') TINYFORMAT_ASSERT(*fmtStart == '%');
{
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
return fmtStart;
}
// Reset stream state to defaults. // Reset stream state to defaults.
out.width(0); out.width(0);
out.precision(6); out.precision(6);
@ -616,18 +680,52 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
bool widthSet = false; bool widthSet = false;
int widthExtra = 0; int widthExtra = 0;
const char* c = fmtStart + 1; const char* c = fmtStart + 1;
// 1) Parse flags
for(;; ++c) // 1) Parse an argument index (if followed by '$') or a width possibly
{ // preceded with '0' flag.
switch(*c) if (*c >= '0' && *c <= '9') {
{ const char tmpc = *c;
int value = parseIntAndAdvance(c);
if (*c == '$') {
// value is an argument index
if (value > 0 && value <= numArgs)
argIndex = value - 1;
else
TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
++c;
positionalMode = true;
}
else if (positionalMode) {
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
}
else {
if (tmpc == '0') {
// Use internal padding so that numeric values are
// formatted correctly, eg -00010 rather than 000-10
out.fill('0');
out.setf(std::ios::internal, std::ios::adjustfield);
}
if (value != 0) {
// Nonzero value means that we parsed width.
widthSet = true;
out.width(value);
}
}
}
else if (positionalMode) {
TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
}
// 2) Parse flags and width if we did not do it in previous step.
if (!widthSet) {
// Parse flags
for (;; ++c) {
switch (*c) {
case '#': case '#':
out.setf(std::ios::showpoint | std::ios::showbase); out.setf(std::ios::showpoint | std::ios::showbase);
continue; continue;
case '0': case '0':
// overridden by left alignment ('-' flag) // overridden by left alignment ('-' flag)
if(!(out.flags() & std::ios::left)) if (!(out.flags() & std::ios::left)) {
{
// Use internal padding so that numeric values are // Use internal padding so that numeric values are
// formatted correctly, eg -00010 rather than 000-10 // formatted correctly, eg -00010 rather than 000-10
out.fill('0'); out.fill('0');
@ -653,63 +751,42 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
} }
break; break;
} }
// 2) Parse width // Parse width
if(*c >= '0' && *c <= '9')
{
widthSet = true;
out.width(parseIntAndAdvance(c));
}
if(*c == '*')
{
widthSet = true;
int width = 0; int width = 0;
if(argIndex < numFormatters) widthSet = parseWidthOrPrecision(width, c, positionalMode,
width = formatters[argIndex++].toInt(); args, argIndex, numArgs);
else if (widthSet) {
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width"); if (width < 0) {
if(width < 0)
{
// negative widths correspond to '-' flag set // negative widths correspond to '-' flag set
out.fill(' '); out.fill(' ');
out.setf(std::ios::left, std::ios::adjustfield); out.setf(std::ios::left, std::ios::adjustfield);
width = -width; width = -width;
} }
out.width(width); out.width(width);
++c; }
} }
// 3) Parse precision // 3) Parse precision
if(*c == '.') if (*c == '.') {
{
++c; ++c;
int precision = 0; int precision = 0;
if(*c == '*') parseWidthOrPrecision(precision, c, positionalMode,
{ args, argIndex, numArgs);
++c; // Presence of `.` indicates precision set, unless the inferred value
if(argIndex < numFormatters) // was negative in which case the default is used.
precision = formatters[argIndex++].toInt(); precisionSet = precision >= 0;
else if (precisionSet)
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
}
else
{
if(*c >= '0' && *c <= '9')
precision = parseIntAndAdvance(c);
else if(*c == '-') // negative precisions ignored, treated as zero.
parseIntAndAdvance(++c);
}
out.precision(precision); out.precision(precision);
precisionSet = true;
} }
// 4) Ignore any C99 length modifier // 4) Ignore any C99 length modifier
while (*c == 'l' || *c == 'h' || *c == 'L' || while (*c == 'l' || *c == 'h' || *c == 'L' ||
*c == 'j' || *c == 'z' || *c == 't') *c == 'j' || *c == 'z' || *c == 't') {
++c; ++c;
}
// 5) We're up to the conversion specifier character. // 5) We're up to the conversion specifier character.
// Set stream flags based on conversion specifier (thanks to the // Set stream flags based on conversion specifier (thanks to the
// boost::format class for forging the way here). // boost::format class for forging the way here).
bool intConversion = false; bool intConversion = false;
switch(*c) switch (*c) {
{
case 'u': case 'd': case 'i': case 'u': case 'd': case 'i':
out.setf(std::ios::dec, std::ios::basefield); out.setf(std::ios::dec, std::ios::basefield);
intConversion = true; intConversion = true;
@ -738,6 +815,18 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
case 'f': case 'f':
out.setf(std::ios::fixed, std::ios::floatfield); out.setf(std::ios::fixed, std::ios::floatfield);
break; break;
case 'A':
out.setf(std::ios::uppercase);
// Falls through
case 'a':
# ifdef _MSC_VER
// Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html
// by always setting maximum precision on MSVC to avoid precision
// loss for doubles.
out.precision(13);
# endif
out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield);
break;
case 'G': case 'G':
out.setf(std::ios::uppercase); out.setf(std::ios::uppercase);
// Falls through // Falls through
@ -746,17 +835,13 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
// As in boost::format, let stream decide float format. // As in boost::format, let stream decide float format.
out.flags(out.flags() & ~std::ios::floatfield); out.flags(out.flags() & ~std::ios::floatfield);
break; break;
case 'a': case 'A':
TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs "
"are not supported");
break;
case 'c': case 'c':
// Handled as special case inside formatValue() // Handled as special case inside formatValue()
break; break;
case 's': case 's':
if (precisionSet) if (precisionSet)
ntrunc = static_cast<int>(out.precision()); ntrunc = static_cast<int>(out.precision());
// Make %s print booleans as "true" and "false" // Make %s print Booleans as "true" and "false"
out.setf(std::ios::boolalpha); out.setf(std::ios::boolalpha);
break; break;
case 'n': case 'n':
@ -770,8 +855,7 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
default: default:
break; break;
} }
if(intConversion && precisionSet && !widthSet) if (intConversion && precisionSet && !widthSet) {
{
// "precision" for integers gives the minimum number of digits (to be // "precision" for integers gives the minimum number of digits (to be
// padded with zeros on the left). This isn't really supported by the // padded with zeros on the left). This isn't really supported by the
// iostreams, but we can approximately simulate it with the width if // iostreams, but we can approximately simulate it with the width if
@ -786,8 +870,8 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
inline void formatImpl(std::ostream& out, const char* fmt, inline void formatImpl(std::ostream& out, const char* fmt,
const detail::FormatArg* formatters, const detail::FormatArg* args,
int numFormatters) int numArgs)
{ {
// Saved stream state // Saved stream state
std::streamsize origWidth = out.width(); std::streamsize origWidth = out.width();
@ -795,26 +879,34 @@ inline void formatImpl(std::ostream& out, const char* fmt,
std::ios::fmtflags origFlags = out.flags(); std::ios::fmtflags origFlags = out.flags();
char origFill = out.fill(); char origFill = out.fill();
for (int argIndex = 0; argIndex < numFormatters; ++argIndex) // "Positional mode" means all format specs should be of the form "%n$..."
{ // with `n` an integer. We detect this in `streamStateFromFormat`.
// Parse the format string bool positionalMode = false;
int argIndex = 0;
while (true) {
fmt = printFormatStringLiteral(out, fmt); fmt = printFormatStringLiteral(out, fmt);
if (*fmt == '\0') {
if (!positionalMode && argIndex < numArgs) {
TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
}
break;
}
bool spacePadPositive = false; bool spacePadPositive = false;
int ntrunc = -1; int ntrunc = -1;
const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt,
formatters, argIndex, numFormatters); args, argIndex, numArgs);
if (argIndex >= numFormatters) // NB: argIndex may be incremented by reading variable width/precision
{ // in `streamStateFromFormat`, so do the bounds check here.
// Check args remain after reading any variable width/precision if (argIndex >= numArgs) {
TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
return; return;
} }
const FormatArg& arg = formatters[argIndex]; const FormatArg& arg = args[argIndex];
// Format the arg into the stream. // Format the arg into the stream.
if(!spacePadPositive) if (!spacePadPositive) {
arg.format(out, fmt, fmtEnd, ntrunc); arg.format(out, fmt, fmtEnd, ntrunc);
else }
{ else {
// The following is a special case with no direct correspondence // The following is a special case with no direct correspondence
// between stream formatting and the printf() behaviour. Simulate // between stream formatting and the printf() behaviour. Simulate
// it crudely by formatting into a temporary string stream and // it crudely by formatting into a temporary string stream and
@ -824,18 +916,17 @@ inline void formatImpl(std::ostream& out, const char* fmt,
tmpStream.setf(std::ios::showpos); tmpStream.setf(std::ios::showpos);
arg.format(tmpStream, fmt, fmtEnd, ntrunc); arg.format(tmpStream, fmt, fmtEnd, ntrunc);
std::string result = tmpStream.str(); // allocates... yuck. std::string result = tmpStream.str(); // allocates... yuck.
for(size_t i = 0, iend = result.size(); i < iend; ++i) for (size_t i = 0, iend = result.size(); i < iend; ++i) {
if(result[i] == '+') result[i] = ' '; if (result[i] == '+')
result[i] = ' ';
}
out << result; out << result;
} }
if (!positionalMode)
++argIndex;
fmt = fmtEnd; fmt = fmtEnd;
} }
// Print remaining part of format string.
fmt = printFormatStringLiteral(out, fmt);
if(*fmt != '\0')
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
// Restore stream state // Restore stream state
out.width(origWidth); out.width(origWidth);
out.precision(origPrecision); out.precision(origPrecision);
@ -855,14 +946,14 @@ inline void formatImpl(std::ostream& out, const char* fmt,
class FormatList class FormatList
{ {
public: public:
FormatList(detail::FormatArg* formatters, int N) FormatList(detail::FormatArg* args, int N)
: m_formatters(formatters), m_N(N) { } : m_args(args), m_N(N) { }
friend void vformat(std::ostream& out, const char* fmt, friend void vformat(std::ostream& out, const char* fmt,
const FormatList& list); const FormatList& list);
private: private:
const detail::FormatArg* m_formatters; const detail::FormatArg* m_args;
int m_N; int m_N;
}; };
@ -879,7 +970,7 @@ class FormatListN : public FormatList
public: public:
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
template<typename... Args> template<typename... Args>
explicit FormatListN(const Args&... args) FormatListN(const Args&... args)
: FormatList(&m_formatterStore[0], N), : FormatList(&m_formatterStore[0], N),
m_formatterStore { FormatArg(args)... } m_formatterStore { FormatArg(args)... }
{ static_assert(sizeof...(args) == N, "Number of args must be N"); } { static_assert(sizeof...(args) == N, "Number of args must be N"); }
@ -888,9 +979,9 @@ class FormatListN : public FormatList
# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ # define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
\ \
template<TINYFORMAT_ARGTYPES(n)> \ template<TINYFORMAT_ARGTYPES(n)> \
explicit FormatListN(TINYFORMAT_VARARGS(n)) \ FormatListN(TINYFORMAT_VARARGS(n)) \
: FormatList(&m_formatterStore[0], n) \ : FormatList(&m_formatterStore[0], n) \
{ assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \ { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
\ \
template<TINYFORMAT_ARGTYPES(n)> \ template<TINYFORMAT_ARGTYPES(n)> \
void init(int i, TINYFORMAT_VARARGS(n)) \ void init(int i, TINYFORMAT_VARARGS(n)) \
@ -902,6 +993,10 @@ class FormatListN : public FormatList
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
# undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
#endif #endif
FormatListN(const FormatListN& other)
: FormatList(&m_formatterStore[0], N)
{ std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N],
&m_formatterStore[0]); }
private: private:
FormatArg m_formatterStore[N]; FormatArg m_formatterStore[N];
@ -956,7 +1051,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
/// list of format arguments is held in a single function argument. /// list of format arguments is held in a single function argument.
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
{ {
detail::formatImpl(out, fmt, list.m_formatters, list.m_N); detail::formatImpl(out, fmt, list.m_args, list.m_N);
} }
@ -993,6 +1088,7 @@ void printfln(const char* fmt, const Args&... args)
std::cout << '\n'; std::cout << '\n';
} }
#else // C++98 version #else // C++98 version
inline void format(std::ostream& out, const char* fmt) inline void format(std::ostream& out, const char* fmt)
@ -1063,6 +1159,7 @@ std::string format(const std::string &fmt, const Args&... args)
} // namespace tinyformat } // namespace tinyformat
// Added for Bitcoin Core:
/** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */ /** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
#define strprintf tfm::format #define strprintf tfm::format