util: Update tinyformat

Updates `tinyformat.h` to commit
3a33bbf654 upstream.

Makes sure that our local changes are kept:

- #3767 1b8fd35aad Make tinyformat errors raise an exception instead of assert()ing
- #4735 9b6d4c5cdc Move strprintf define to tinyformat.h
- #4748 6e5fd003e0 include stdexcept (for std::exception)
- #8000 9eaa0afa6e force USE_VARIADIC_TEMPLATES
- Add `std::string format(const std::string &fmt...` added this
  at the time, as we want to be able to do `strprintf(_(...), ...)`

Inspired by #8264.
This commit is contained in:
Wladimir J. van der Laan 2016-06-27 17:06:47 +02:00
parent 5a06ebbf2d
commit 695041e495

View file

@ -67,7 +67,9 @@
// weekday, month, day, hour, min); // weekday, month, day, hour, min);
// std::cout << date; // std::cout << date;
// //
// These are the three primary interface functions. // These are the three primary interface functions. There is also a
// convenience function printfln() which appends a newline to the usual result
// of printf() for super simple logging.
// //
// //
// User defined format functions // User defined format functions
@ -86,6 +88,18 @@
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an
// example, see the implementation of printf() at the end of the source file. // example, see the implementation of printf() at the end of the source file.
// //
// Sometimes it's useful to be able to pass a list of format arguments through
// to a non-template function. The FormatList class is provided as a way to do
// this by storing the argument list in a type-opaque way. Continuing the
// example from above, we construct a FormatList using makeFormatList():
//
// FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
//
// The format list can now be passed into any non-template function and used
// via a call to the vformat() function:
//
// tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
//
// //
// Additional API information // Additional API information
// -------------------------- // --------------------------
@ -118,6 +132,7 @@ namespace tfm = tinyformat;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Implementation details. // Implementation details.
#include <algorithm>
#include <cassert> #include <cassert>
#include <iostream> #include <iostream>
#include <sstream> #include <sstream>
@ -133,20 +148,20 @@ namespace tfm = tinyformat;
# endif # endif
#endif #endif
#ifdef __GNUC__
# define TINYFORMAT_NOINLINE __attribute__((noinline))
#elif defined(_MSC_VER)
# define TINYFORMAT_NOINLINE __declspec(noinline)
#else
# define TINYFORMAT_NOINLINE
#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 OSX. 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__
// Workaround OSX linker warning: xcode uses different default symbol
// visibilities for static libs vs executables (see issue #25)
# define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
#else
# define TINYFORMAT_HIDDEN
#endif
namespace tinyformat { namespace tinyformat {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
@ -247,6 +262,29 @@ struct convertToInt<T,true>
static int invoke(const T& value) { return static_cast<int>(value); } static int invoke(const T& value) { return static_cast<int>(value); }
}; };
// Format at most ntrunc characters to the given stream.
template<typename T>
inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
{
std::ostringstream tmp;
tmp << value;
std::string result = tmp.str();
out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
}
#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type) \
inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
{ \
std::streamsize len = 0; \
while(len < ntrunc && value[len] != 0) \
++len; \
out.write(value, len); \
}
// Overload for const char* and char*. Could overload for signed & unsigned
// char too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
} // namespace detail } // namespace detail
@ -255,18 +293,20 @@ struct convertToInt<T,true>
// desired. // desired.
// Format a value into a stream. Called from format() for all types by default. /// Format a value into a stream, delegating to operator<< by default.
// ///
// Users may override this for their own types. When this function is called, /// Users may override this for their own types. When this function is called,
// the stream flags will have been modified according to the format string. /// the stream flags will have been modified according to the format string.
// The format specification is provided in the range [fmtBegin, fmtEnd). /// The format specification is provided in the range [fmtBegin, fmtEnd). For
// /// truncating conversions, ntrunc is set to the desired maximum number of
// By default, formatValue() uses the usual stream insertion operator /// characters, for example "%.7s" calls formatValue with ntrunc = 7.
// operator<< to format the type T, with special cases for the %c and %p ///
// conversions. /// By default, formatValue() uses the usual stream insertion operator
/// operator<< to format the type T, with special cases for the %c and %p
/// conversions.
template<typename T> template<typename T>
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
const char* fmtEnd, const T& value) const char* fmtEnd, int ntrunc, const T& value)
{ {
#ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS #ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
// Since we don't support printing of wchar_t using "%ls", make it fail at // Since we don't support printing of wchar_t using "%ls", make it fail at
@ -288,6 +328,12 @@ 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)
{
// Take care not to overread C strings in truncating conversions like
// "%.4s" where at most 4 characters may be read.
detail::formatTruncated(out, value, ntrunc);
}
else else
out << value; out << value;
} }
@ -296,7 +342,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
// Overloaded version for char types to support printing as an integer // Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \
inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \
const char* fmtEnd, charType value) \ const char* fmtEnd, int /**/, charType value) \
{ \ { \
switch(*(fmtEnd-1)) \ switch(*(fmtEnd-1)) \
{ \ { \
@ -435,225 +481,91 @@ cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' +
namespace detail { namespace detail {
// Class holding current position in format string and an output stream into // Type-opaque holder for an argument to format(), with associated actions on
// which arguments are formatted. // the type held as explicit function pointers. This allows FormatArg's for
class FormatIterator // each argument to be allocated as a homogenous array inside FormatList
// whereas a naive implementation based on inheritance does not.
class FormatArg
{ {
public: public:
// Flags for features not representable with standard stream state FormatArg() {}
enum ExtraFormatFlags
{
Flag_None = 0,
Flag_TruncateToPrecision = 1<<0, // truncate length to stream precision()
Flag_SpacePadPositive = 1<<1, // pad positive values with spaces
Flag_VariableWidth = 1<<2, // variable field width in arg list
Flag_VariablePrecision = 1<<3 // variable field precision in arg list
};
// out is the output stream, fmt is the full format string template<typename T>
FormatIterator(std::ostream& out, const char* fmt) FormatArg(const T& value)
: m_out(out), : m_value(static_cast<const void*>(&value)),
m_fmt(fmt), m_formatImpl(&formatImpl<T>),
m_extraFlags(Flag_None), m_toIntImpl(&toIntImpl<T>)
m_wantWidth(false),
m_wantPrecision(false),
m_variableWidth(0),
m_variablePrecision(0),
m_origWidth(out.width()),
m_origPrecision(out.precision()),
m_origFlags(out.flags()),
m_origFill(out.fill())
{ } { }
// Print remaining part of format string. void format(std::ostream& out, const char* fmtBegin,
void finish() const char* fmtEnd, int ntrunc) const
{ {
// It would be nice if we could do this from the destructor, but we m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
// can't if TINFORMAT_ERROR is used to throw an exception!
m_fmt = printFormatStringLiteral(m_out, m_fmt);
if(*m_fmt != '\0')
TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
} }
~FormatIterator() int toInt() const
{ {
// Restore stream state return m_toIntImpl(m_value);
m_out.width(m_origWidth); }
m_out.precision(m_origPrecision);
m_out.flags(m_origFlags); private:
m_out.fill(m_origFill); template<typename T>
TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value)
{
formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
} }
template<typename T> template<typename T>
void accept(const T& value); TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
private:
// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
static int parseIntAndAdvance(const char*& c)
{ {
return convertToInt<T>::invoke(*static_cast<const T*>(value));
}
const void* m_value;
void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
const char* fmtEnd, int ntrunc, const void* value);
int (*m_toIntImpl)(const void* value);
};
// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
inline int parseIntAndAdvance(const char*& c)
{
int i = 0; int i = 0;
for(;*c >= '0' && *c <= '9'; ++c) for(;*c >= '0' && *c <= '9'; ++c)
i = 10*i + (*c - '0'); i = 10*i + (*c - '0');
return i; return i;
} }
// Format at most truncLen characters of a C string to the given // Print literal part of format string and return next format spec
// stream. Return true if formatting proceeded (generic version always // position.
// returns false) //
template<typename T> // Skips over any occurrences of '%%', printing a literal '%' to the
static bool formatCStringTruncate(std::ostream& /*out*/, const T& /*value*/, // output. The position of the first % character of the next
std::streamsize /*truncLen*/) // nontrivial format spec is returned, or the end of string.
{ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
return false; {
}
# define TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(type) \
static bool formatCStringTruncate(std::ostream& out, type* value, \
std::streamsize truncLen) \
{ \
std::streamsize len = 0; \
while(len < truncLen && value[len] != 0) \
++len; \
out.write(value, len); \
return true; \
}
// Overload for const char* and char*. Could overload for signed &
// unsigned char too, but these are technically unneeded for printf
// compatibility.
TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(const char)
TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE(char)
# undef TINYFORMAT_DEFINE_FORMAT_C_STRING_TRUNCATE
// Print literal part of format string and return next format spec
// position.
//
// Skips over any occurrences of '%%', printing a literal '%' to the
// output. The position of the first % character of the next
// nontrivial format spec is returned, or the end of string.
static const char* printFormatStringLiteral(std::ostream& out,
const char* fmt)
{
const char* c = fmt; const char* c = fmt;
for(; true; ++c) for(;; ++c)
{ {
switch(*c) switch(*c)
{ {
case '\0': case '\0':
out.write(fmt, static_cast<std::streamsize>(c - fmt)); out.write(fmt, c - fmt);
return c; return c;
case '%': case '%':
out.write(fmt, static_cast<std::streamsize>(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; break;
default:
break;
} }
} }
}
static const char* streamStateFromFormat(std::ostream& out,
unsigned int& extraFlags,
const char* fmtStart,
int variableWidth,
int variablePrecision);
// Private copy & assign: Kill gcc warnings with -Weffc++
FormatIterator(const FormatIterator&);
FormatIterator& operator=(const FormatIterator&);
// Stream, current format string & state
std::ostream& m_out;
const char* m_fmt;
unsigned int m_extraFlags;
// State machine info for handling of variable width & precision
bool m_wantWidth;
bool m_wantPrecision;
int m_variableWidth;
int m_variablePrecision;
// Saved stream state
std::streamsize m_origWidth;
std::streamsize m_origPrecision;
std::ios::fmtflags m_origFlags;
char m_origFill;
};
// Accept a value for formatting into the internal stream.
template<typename T>
TINYFORMAT_NOINLINE // < greatly reduces bloat in optimized builds
void FormatIterator::accept(const T& value)
{
// Parse the format string
const char* fmtEnd = 0;
if(m_extraFlags == Flag_None && !m_wantWidth && !m_wantPrecision)
{
m_fmt = printFormatStringLiteral(m_out, m_fmt);
fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt, 0, 0);
m_wantWidth = (m_extraFlags & Flag_VariableWidth) != 0;
m_wantPrecision = (m_extraFlags & Flag_VariablePrecision) != 0;
}
// Consume value as variable width and precision specifier if necessary
if(m_extraFlags & (Flag_VariableWidth | Flag_VariablePrecision))
{
if(m_wantWidth || m_wantPrecision)
{
int v = convertToInt<T>::invoke(value);
if(m_wantWidth)
{
m_variableWidth = v;
m_wantWidth = false;
}
else if(m_wantPrecision)
{
m_variablePrecision = v;
m_wantPrecision = false;
}
return;
}
// If we get here, we've set both the variable precision and width as
// required and we need to rerun the stream state setup to insert these.
fmtEnd = streamStateFromFormat(m_out, m_extraFlags, m_fmt,
m_variableWidth, m_variablePrecision);
}
// Format the value into the stream.
if(!(m_extraFlags & (Flag_SpacePadPositive | Flag_TruncateToPrecision)))
formatValue(m_out, m_fmt, fmtEnd, value);
else
{
// The following are special cases where there's no direct
// correspondence between stream formatting and the printf() behaviour.
// Instead, we simulate the behaviour crudely by formatting into a
// temporary string stream and munging the resulting string.
std::ostringstream tmpStream;
tmpStream.copyfmt(m_out);
if(m_extraFlags & Flag_SpacePadPositive)
tmpStream.setf(std::ios::showpos);
// formatCStringTruncate is required for truncating conversions like
// "%.4s" where at most 4 characters of the c-string should be read.
// If we didn't include this special case, we might read off the end.
if(!( (m_extraFlags & Flag_TruncateToPrecision) &&
formatCStringTruncate(tmpStream, value, m_out.precision()) ))
{
// Not a truncated c-string; just format normally.
formatValue(tmpStream, m_fmt, fmtEnd, value);
}
std::string result = tmpStream.str(); // allocates... yuck.
if(m_extraFlags & Flag_SpacePadPositive)
{
for(size_t i = 0, iend = result.size(); i < iend; ++i)
if(result[i] == '+')
result[i] = ' ';
}
if((m_extraFlags & Flag_TruncateToPrecision) &&
(int)result.size() > (int)m_out.precision())
m_out.write(result.c_str(), m_out.precision());
else
m_out << result;
}
m_extraFlags = Flag_None;
m_fmt = fmtEnd;
} }
@ -663,13 +575,14 @@ void FormatIterator::accept(const T& value)
// with the form "%[flags][width][.precision][length]type". // with the form "%[flags][width][.precision][length]type".
// //
// 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 the extraFlags parameter which is a bitwise // state are returned in spacePadPositive (for space padded positive numbers)
// combination of values from the ExtraFormatFlags enum. // and ntrunc (for truncating conversions). argIndex is incremented if
inline const char* FormatIterator::streamStateFromFormat(std::ostream& out, // necessary to pull out variable width and precision . The function returns a
unsigned int& extraFlags, // pointer to the character after the end of the current format spec.
const char* fmtStart, inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositive,
int variableWidth, int& ntrunc, const char* fmtStart,
int variablePrecision) const detail::FormatArg* formatters,
int& argIndex, int numFormatters)
{ {
if(*fmtStart != '%') if(*fmtStart != '%')
{ {
@ -684,9 +597,9 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
out.unsetf(std::ios::adjustfield | std::ios::basefield | out.unsetf(std::ios::adjustfield | std::ios::basefield |
std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
std::ios::showpoint | std::ios::showpos | std::ios::uppercase); std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
extraFlags = Flag_None;
bool precisionSet = false; bool precisionSet = false;
bool widthSet = false; bool widthSet = false;
int widthExtra = 0;
const char* c = fmtStart + 1; const char* c = fmtStart + 1;
// 1) Parse flags // 1) Parse flags
for(;; ++c) for(;; ++c)
@ -713,12 +626,15 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
case ' ': case ' ':
// overridden by show positive sign, '+' flag. // overridden by show positive sign, '+' flag.
if(!(out.flags() & std::ios::showpos)) if(!(out.flags() & std::ios::showpos))
extraFlags |= Flag_SpacePadPositive; spacePadPositive = true;
continue; continue;
case '+': case '+':
out.setf(std::ios::showpos); out.setf(std::ios::showpos);
extraFlags &= ~Flag_SpacePadPositive; spacePadPositive = false;
widthExtra = 1;
continue; continue;
default:
break;
} }
break; break;
} }
@ -731,15 +647,19 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*') if(*c == '*')
{ {
widthSet = true; widthSet = true;
if(variableWidth < 0) int width = 0;
if(argIndex < numFormatters)
width = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width");
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);
variableWidth = -variableWidth; width = -width;
} }
out.width(variableWidth); out.width(width);
extraFlags |= Flag_VariableWidth;
++c; ++c;
} }
// 3) Parse precision // 3) Parse precision
@ -750,8 +670,10 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
if(*c == '*') if(*c == '*')
{ {
++c; ++c;
extraFlags |= Flag_VariablePrecision; if(argIndex < numFormatters)
precision = variablePrecision; precision = formatters[argIndex++].toInt();
else
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable precision");
} }
else else
{ {
@ -814,7 +736,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
break; break;
case 's': case 's':
if(precisionSet) if(precisionSet)
extraFlags |= Flag_TruncateToPrecision; 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;
@ -826,6 +748,8 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
"terminated by end of string"); "terminated by end of string");
return c; return c;
default:
break;
} }
if(intConversion && precisionSet && !widthSet) if(intConversion && precisionSet && !widthSet)
{ {
@ -833,7 +757,7 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
// 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
// the width isn't otherwise used. // the width isn't otherwise used.
out.width(out.precision()); out.width(out.precision() + widthExtra);
out.setf(std::ios::internal, std::ios::adjustfield); out.setf(std::ios::internal, std::ios::adjustfield);
out.fill('0'); out.fill('0');
} }
@ -841,170 +765,282 @@ inline const char* FormatIterator::streamStateFromFormat(std::ostream& out,
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Private format function on top of which the public interface is implemented. inline void formatImpl(std::ostream& out, const char* fmt,
// We enforce a mimimum of one value to be formatted to prevent bugs looking like const detail::FormatArg* formatters,
// int numFormatters)
// const char* myStr = "100% broken"; {
// printf(myStr); // Parses % as a format specifier // Saved stream state
std::streamsize origWidth = out.width();
std::streamsize origPrecision = out.precision();
std::ios::fmtflags origFlags = out.flags();
char origFill = out.fill();
for (int argIndex = 0; argIndex < numFormatters; ++argIndex)
{
// Parse the format string
fmt = printFormatStringLiteral(out, fmt);
bool spacePadPositive = false;
int ntrunc = -1;
const char* fmtEnd = streamStateFromFormat(out, spacePadPositive, ntrunc, fmt,
formatters, argIndex, numFormatters);
if (argIndex >= numFormatters)
{
// Check args remain after reading any variable width/precision
TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
return;
}
const FormatArg& arg = formatters[argIndex];
// Format the arg into the stream.
if(!spacePadPositive)
arg.format(out, fmt, fmtEnd, ntrunc);
else
{
// The following is a special case with no direct correspondence
// between stream formatting and the printf() behaviour. Simulate
// it crudely by formatting into a temporary string stream and
// munging the resulting string.
std::ostringstream tmpStream;
tmpStream.copyfmt(out);
tmpStream.setf(std::ios::showpos);
arg.format(tmpStream, fmt, fmtEnd, ntrunc);
std::string result = tmpStream.str(); // allocates... yuck.
for(size_t i = 0, iend = result.size(); i < iend; ++i)
if(result[i] == '+') result[i] = ' ';
out << result;
}
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
out.width(origWidth);
out.precision(origPrecision);
out.flags(origFlags);
out.fill(origFill);
}
} // namespace detail
/// List of template arguments format(), held in a type-opaque way.
///
/// A const reference to FormatList (typedef'd as FormatListRef) may be
/// conveniently used to pass arguments to non-template functions: All type
/// information has been stripped from the arguments, leaving just enough of a
/// common interface to perform formatting as required.
class FormatList
{
public:
FormatList(detail::FormatArg* formatters, int N)
: m_formatters(formatters), m_N(N) { }
friend void vformat(std::ostream& out, const char* fmt,
const FormatList& list);
private:
const detail::FormatArg* m_formatters;
int m_N;
};
/// Reference to type-opaque format list for passing to vformat()
typedef const FormatList& FormatListRef;
namespace detail {
// Format list subclass with fixed storage to avoid dynamic allocation
template<int N>
class FormatListN : public FormatList
{
public:
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
template<typename... Args>
FormatListN(const Args&... args)
: FormatList(&m_formatterStore[0], N),
m_formatterStore { FormatArg(args)... }
{ static_assert(sizeof...(args) == N, "Number of args must be N"); }
#else // C++98 version
void init(int) {}
# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \
\
template<TINYFORMAT_ARGTYPES(n)> \
FormatListN(TINYFORMAT_VARARGS(n)) \
: FormatList(&m_formatterStore[0], n) \
{ assert(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
\
template<TINYFORMAT_ARGTYPES(n)> \
void init(int i, TINYFORMAT_VARARGS(n)) \
{ \
m_formatterStore[i] = FormatArg(v1); \
init(i+1 TINYFORMAT_PASSARGS_TAIL(n)); \
}
template<typename T1> TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
void format(FormatIterator& fmtIter, const T1& value1) # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
#endif
private:
FormatArg m_formatterStore[N];
};
// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
template<> class FormatListN<0> : public FormatList
{ {
fmtIter.accept(value1); public: FormatListN() : FormatList(0, 0) {}
fmtIter.finish(); };
}
// General version for C++11
template<typename T1, typename... Args>
void format(FormatIterator& fmtIter, const T1& value1, const Args&... args)
{
fmtIter.accept(value1);
format(fmtIter, args...);
}
#else
inline void format(FormatIterator& fmtIter)
{
fmtIter.finish();
}
// General version for C++98
#define TINYFORMAT_MAKE_FORMAT_DETAIL(n) \
template<TINYFORMAT_ARGTYPES(n)> \
void format(detail::FormatIterator& fmtIter, TINYFORMAT_VARARGS(n)) \
{ \
fmtIter.accept(v1); \
format(fmtIter TINYFORMAT_PASSARGS_TAIL(n)); \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_DETAIL)
#undef TINYFORMAT_MAKE_FORMAT_DETAIL
#endif // End C++98 variadic template emulation for format()
} // namespace detail } // namespace detail
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Implement all the main interface functions here in terms of detail::format() // Primary API functions
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
// C++11 - the simple case /// Make type-agnostic format list from list of template arguments.
template<typename T1, typename... Args> ///
void format(std::ostream& out, const char* fmt, const T1& v1, const Args&... args) /// The exact return type of this function is an implementation detail and
/// shouldn't be relied upon. Instead it should be stored as a FormatListRef:
///
/// FormatListRef formatList = makeFormatList( /*...*/ );
template<typename... Args>
detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
{ {
detail::FormatIterator fmtIter(out, fmt); return detail::FormatListN<sizeof...(args)>(args...);
format(fmtIter, v1, args...);
} }
template<typename T1, typename... Args> #else // C++98 version
std::string format(const char* fmt, const T1& v1, const Args&... args)
inline detail::FormatListN<0> makeFormatList()
{
return detail::FormatListN<0>();
}
#define TINYFORMAT_MAKE_MAKEFORMATLIST(n) \
template<TINYFORMAT_ARGTYPES(n)> \
detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n)) \
{ \
return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n)); \
}
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
#undef TINYFORMAT_MAKE_MAKEFORMATLIST
#endif
/// Format list of arguments to the stream according to the given format string.
///
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
/// list of format arguments is held in a single function argument.
inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
{
detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
}
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
/// Format list of arguments to the stream according to given format string.
template<typename... Args>
void format(std::ostream& out, const char* fmt, const Args&... args)
{
vformat(out, fmt, makeFormatList(args...));
}
/// Format list of arguments according to the given format string and return
/// the result as a string.
template<typename... Args>
std::string format(const char* fmt, const Args&... args)
{ {
std::ostringstream oss; std::ostringstream oss;
format(oss, fmt, v1, args...); format(oss, fmt, args...);
return oss.str(); return oss.str();
} }
template<typename T1, typename... Args> /// Format list of arguments to std::cout, according to the given format string
std::string format(const std::string &fmt, const T1& v1, const Args&... args) template<typename... Args>
void printf(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
}
template<typename... Args>
void printfln(const char* fmt, const Args&... args)
{
format(std::cout, fmt, args...);
std::cout << '\n';
}
#else // C++98 version
inline void format(std::ostream& out, const char* fmt)
{
vformat(out, fmt, makeFormatList());
}
inline std::string format(const char* fmt)
{ {
std::ostringstream oss; std::ostringstream oss;
format(oss, fmt.c_str(), v1, args...); format(oss, fmt);
return oss.str(); return oss.str();
} }
template<typename T1, typename... Args> inline void printf(const char* fmt)
void printf(const char* fmt, const T1& v1, const Args&... args)
{ {
format(std::cout, fmt, v1, args...); format(std::cout, fmt);
} }
#else inline void printfln(const char* fmt)
{
format(std::cout, fmt);
std::cout << '\n';
}
// C++98 - define the interface functions using the wrapping macros
#define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ #define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \
\ \
template<TINYFORMAT_ARGTYPES(n)> \ template<TINYFORMAT_ARGTYPES(n)> \
void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \ { \
tinyformat::detail::FormatIterator fmtIter(out, fmt); \ vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \
tinyformat::detail::format(fmtIter, TINYFORMAT_PASSARGS(n)); \
} \ } \
\ \
template<TINYFORMAT_ARGTYPES(n)> \ template<TINYFORMAT_ARGTYPES(n)> \
std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \ std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \ { \
std::ostringstream oss; \ std::ostringstream oss; \
tinyformat::format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
return oss.str(); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
std::string format(const std::string &fmt, TINYFORMAT_VARARGS(n)) \
{ \
std::ostringstream oss; \
tinyformat::format(oss, fmt.c_str(), TINYFORMAT_PASSARGS(n)); \
return oss.str(); \ return oss.str(); \
} \ } \
\ \
template<TINYFORMAT_ARGTYPES(n)> \ template<TINYFORMAT_ARGTYPES(n)> \
void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \ void printf(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \ { \
tinyformat::format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
} \
\
template<TINYFORMAT_ARGTYPES(n)> \
void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \
{ \
format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \
std::cout << '\n'; \
} }
TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS) TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
#undef TINYFORMAT_MAKE_FORMAT_FUNCS #undef TINYFORMAT_MAKE_FORMAT_FUNCS
#endif #endif
// Added for Bitcoin Core
//------------------------------------------------------------------------------ template<typename... Args>
// Define deprecated wrapping macro for backward compatibility in tinyformat std::string format(const std::string &fmt, const Args&... args)
// 1.x. Will be removed in version 2! {
#define TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS std::ostringstream oss;
#define TINYFORMAT_WRAP_FORMAT_N(n, returnType, funcName, funcDeclSuffix, \ format(oss, fmt.c_str(), args...);
bodyPrefix, streamName, bodySuffix) \ return oss.str();
template<TINYFORMAT_ARGTYPES(n)> \ }
returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt, \
TINYFORMAT_VARARGS(n)) funcDeclSuffix \
{ \
bodyPrefix \
tinyformat::format(streamName, fmt, TINYFORMAT_PASSARGS(n)); \
bodySuffix \
} \
#define TINYFORMAT_WRAP_FORMAT(returnType, funcName, funcDeclSuffix, \
bodyPrefix, streamName, bodySuffix) \
inline \
returnType funcName(TINYFORMAT_WRAP_FORMAT_EXTRA_ARGS const char* fmt \
) funcDeclSuffix \
{ \
bodyPrefix \
tinyformat::detail::FormatIterator(streamName, fmt).finish(); \
bodySuffix \
} \
TINYFORMAT_WRAP_FORMAT_N(1 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(2 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(3 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(4 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(5 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(6 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(7 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(8 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(9 , returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(10, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(11, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(12, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(13, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(14, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(15, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
TINYFORMAT_WRAP_FORMAT_N(16, returnType, funcName, funcDeclSuffix, bodyPrefix, streamName, bodySuffix) \
} // namespace tinyformat } // namespace tinyformat