Changeset View
Changeset View
Standalone View
Standalone View
src/tinyformat.h
Show All 27 Lines | |||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// Tinyformat: A minimal type safe printf replacement | // Tinyformat: A minimal type safe printf replacement | ||||
// | // | ||||
// tinyformat.h is a type safe printf replacement library in a single C++ | // tinyformat.h is a type safe printf replacement library in a single C++ | ||||
// header file. Design goals include: | // header file. Design goals include: | ||||
// | // | ||||
// * 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 | ||||
// * C++98 support, with optional C++11 niceties | // * C++98 support, with optional C++11 niceties | ||||
// | // | ||||
// | // | ||||
// 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"; | ||||
// size_t day = 27; | // size_t day = 27; | ||||
// long hour = 14; | // long hour = 14; | ||||
// int min = 44; | // int min = 44; | ||||
// | // | ||||
// 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 | ||||
// using either of the tfm::format() functions. One prints on a user provided | // using either of the tfm::format() functions. One prints on a user provided | ||||
// stream: | // stream: | ||||
// | // | ||||
// tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", | // tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", | ||||
// weekday, month, day, hour, min); | // weekday, month, day, hour, min); | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | |||||
// Define for C++11 variadic templates which make the code shorter & more | // Define for C++11 variadic templates which make the code shorter & more | ||||
// general. If you don't define this, C++11 support is autodetected below. | // general. If you don't define this, C++11 support is autodetected below. | ||||
#define TINYFORMAT_USE_VARIADIC_TEMPLATES | #define TINYFORMAT_USE_VARIADIC_TEMPLATES | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
// 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 | ||||
#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 | ||||
#if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && \ | #if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && \ | ||||
!defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) | !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES) | ||||
#ifdef __GXX_EXPERIMENTAL_CXX0X__ | #ifdef __GXX_EXPERIMENTAL_CXX0X__ | ||||
#define TINYFORMAT_USE_VARIADIC_TEMPLATES | #define TINYFORMAT_USE_VARIADIC_TEMPLATES | ||||
#endif | #endif | ||||
#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 | ||||
#define TINYFORMAT_HIDDEN | #define TINYFORMAT_HIDDEN | ||||
#endif | #endif | ||||
namespace tinyformat { | namespace tinyformat { | ||||
// Added for Bitcoin | |||||
class format_error : public std::runtime_error { | class format_error : public std::runtime_error { | ||||
public: | public: | ||||
explicit format_error(const std::string &what) : std::runtime_error(what) {} | explicit format_error(const std::string &what) : std::runtime_error(what) {} | ||||
}; | }; | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
namespace detail { | namespace detail { | ||||
Show All 40 Lines | #endif | ||||
template <int n> struct is_wchar<wchar_t[n]> {}; | template <int n> struct is_wchar<wchar_t[n]> {}; | ||||
// Format the value by casting to type fmtT. This default implementation | // Format the value by casting to type fmtT. This default implementation | ||||
// should never be called. | // should never be called. | ||||
template <typename T, typename fmtT, | template <typename T, typename fmtT, | ||||
bool convertible = is_convertible<T, fmtT>::value> | bool convertible = is_convertible<T, fmtT>::value> | ||||
struct formatValueAsType { | struct formatValueAsType { | ||||
static void invoke(std::ostream & /*out*/, const T & /*value*/) { | static void invoke(std::ostream & /*out*/, const T & /*value*/) { | ||||
assert(0); | 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. | ||||
template <typename T, typename fmtT> | template <typename T, typename fmtT> | ||||
struct formatValueAsType<T, fmtT, true> { | struct formatValueAsType<T, fmtT, true> { | ||||
static void invoke(std::ostream &out, const T &value) { | static void invoke(std::ostream &out, const T &value) { | ||||
out << static_cast<fmtT>(value); | out << static_cast<fmtT>(value); | ||||
} | } | ||||
}; | }; | ||||
▲ Show 20 Lines • Show All 312 Lines • ▼ Show 20 Lines | #define TINYFORMAT_FOREACH_ARGNUM(m) \ | ||||
m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) \ | m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) \ | ||||
m(15) m(16) | m(15) m(16) | ||||
//[[[end]]] | //[[[end]]] | ||||
namespace detail { | namespace detail { | ||||
// Type-opaque holder for an argument to format(), with associated actions | // Type-opaque holder for an argument to format(), with associated actions | ||||
// on the type held as explicit function pointers. This allows FormatArg's | // on the type held as explicit function pointers. This allows FormatArg's | ||||
// for each argument to be allocated as a homogenous array inside FormatList | // for each argument to be allocated as a homogeneous array inside | ||||
// whereas a naive implementation based on inheritance does not. | // FormatList whereas a naive implementation based on inheritance does not. | ||||
class FormatArg { | class FormatArg { | ||||
public: | public: | ||||
FormatArg() | FormatArg() : m_value(NULL), m_formatImpl(NULL), m_toIntImpl(NULL) {} | ||||
: m_value(nullptr), m_formatImpl(nullptr), m_toIntImpl(nullptr) {} | |||||
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_toIntImpl(&toIntImpl<T>) {} | m_formatImpl(&formatImpl<T>), m_toIntImpl(&toIntImpl<T>) {} | ||||
void format(std::ostream &out, const char *fmtBegin, const char *fmtEnd, | void format(std::ostream &out, const char *fmtBegin, const char *fmtEnd, | ||||
int ntrunc) const { | 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); | ||||
} | } | ||||
private: | private: | ||||
template <typename T> | template <typename T> | ||||
TINYFORMAT_HIDDEN static void | TINYFORMAT_HIDDEN static void | ||||
formatImpl(std::ostream &out, const char *fmtBegin, const char *fmtEnd, | formatImpl(std::ostream &out, const char *fmtBegin, const char *fmtEnd, | ||||
int ntrunc, const void *value) { | int ntrunc, const void *value) { | ||||
Show All 16 Lines | namespace detail { | ||||
// On return, c is set to one past the end of the integer. | // On return, c is set to one past the end of the integer. | ||||
inline int parseIntAndAdvance(const char *&c) { | 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; | ||||
} | } | ||||
// Parse width or precision `n` from format string pointer `c`, and advance | |||||
// it 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. | // Print literal part of format string and return next format spec position. | ||||
// | // | ||||
// Skips over any occurrences of '%%', printing a literal '%' to the output. | // Skips over any occurrences of '%%', printing a literal '%' to the output. | ||||
// The position of the first % character of the next nontrivial format spec | // The position of the first % character of the next nontrivial format spec | ||||
// is returned, or the end of string. | // is returned, or the end of string. | ||||
inline const char *printFormatStringLiteral(std::ostream &out, | inline const char *printFormatStringLiteral(std::ostream &out, | ||||
const char *fmt) { | const char *fmt) { | ||||
const char *c = fmt; | const char *c = fmt; | ||||
for (;; ++c) { | for (;; ++c) { | ||||
switch (*c) { | if (*c == '\0') { | ||||
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) != '%') return c; | if (*(c + 1) != '%') return c; | ||||
// for "%%", tack trailing % onto next literal section. | // for "%%", tack trailing % onto next literal section. | ||||
fmt = ++c; | fmt = ++c; | ||||
break; | |||||
default: | |||||
break; | |||||
} | } | ||||
} | } | ||||
} | } | ||||
// 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 | // state are returned in spacePadPositive (for space padded positive | ||||
// numbers) and ntrunc (for truncating conversions). argIndex is incremented | // numbers) and ntrunc (for truncating conversions). argIndex is incremented | ||||
// if necessary to pull out variable width and precision. The function | // if necessary to pull out variable width and precision. The function | ||||
// returns a pointer to the character after the end of the current format | // returns a pointer to the character after the end of the current format | ||||
// spec. | // spec. | ||||
inline const char * | inline const char *streamStateFromFormat(std::ostream &out, | ||||
streamStateFromFormat(std::ostream &out, bool &spacePadPositive, | bool &positionalMode, | ||||
bool &spacePadPositive, | |||||
int &ntrunc, const char *fmtStart, | int &ntrunc, const char *fmtStart, | ||||
const detail::FormatArg *formatters, int &argIndex, | const detail::FormatArg *args, | ||||
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); | ||||
out.fill(' '); | out.fill(' '); | ||||
// Reset most flags; ignore irrelevant unitbuf & skipws. | // Reset most flags; ignore irrelevant unitbuf & skipws. | ||||
out.unsetf(std::ios::adjustfield | std::ios::basefield | | out.unsetf(std::ios::adjustfield | std::ios::basefield | | ||||
std::ios::floatfield | std::ios::showbase | | std::ios::floatfield | std::ios::showbase | | ||||
std::ios::boolalpha | std::ios::showpoint | | std::ios::boolalpha | std::ios::showpoint | | ||||
std::ios::showpos | std::ios::uppercase); | std::ios::showpos | std::ios::uppercase); | ||||
bool precisionSet = false; | bool precisionSet = false; | ||||
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 | |||||
// 1) Parse an argument index (if followed by '$') or a width possibly | |||||
// preceded with '0' flag. | |||||
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) { | for (;; ++c) { | ||||
switch (*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'); | ||||
out.setf(std::ios::internal, std::ios::adjustfield); | out.setf(std::ios::internal, std::ios::adjustfield); | ||||
} | } | ||||
continue; | continue; | ||||
case '-': | case '-': | ||||
out.fill(' '); | out.fill(' '); | ||||
out.setf(std::ios::left, std::ios::adjustfield); | out.setf(std::ios::left, std::ios::adjustfield); | ||||
continue; | continue; | ||||
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)) | ||||
spacePadPositive = true; | spacePadPositive = true; | ||||
continue; | continue; | ||||
case '+': | case '+': | ||||
out.setf(std::ios::showpos); | out.setf(std::ios::showpos); | ||||
spacePadPositive = false; | spacePadPositive = false; | ||||
widthExtra = 1; | widthExtra = 1; | ||||
continue; | continue; | ||||
default: | default: | ||||
break; | break; | ||||
} | } | ||||
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, args, | ||||
width = formatters[argIndex++].toInt(); | 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, | ||||
++c; | numArgs); | ||||
if (argIndex < numFormatters) | // Presence of `.` indicates precision set, unless the inferred | ||||
precision = formatters[argIndex++].toInt(); | // value was negative in which case the default is used. | ||||
else | precisionSet = precision >= 0; | ||||
TINYFORMAT_ERROR("tinyformat: Not enough arguments to read " | if (precisionSet) out.precision(precision); | ||||
"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); | |||||
precisionSet = true; | |||||
} | } | ||||
// 4) Ignore any C99 length modifier | // 4) Ignore any C99 length modifier | ||||
while (*c == 'l' || *c == 'h' || *c == 'L' || *c == 'j' || *c == 'z' || | while (*c == 'l' || *c == 'h' || *c == 'L' || *c == 'j' || *c == 'z' || | ||||
*c == 't') | *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 'u': | ||||
case 'd': | case 'd': | ||||
case 'i': | case 'i': | ||||
out.setf(std::ios::dec, std::ios::basefield); | out.setf(std::ios::dec, std::ios::basefield); | ||||
intConversion = true; | intConversion = true; | ||||
break; | break; | ||||
case 'o': | case 'o': | ||||
out.setf(std::ios::oct, std::ios::basefield); | out.setf(std::ios::oct, std::ios::basefield); | ||||
intConversion = true; | intConversion = true; | ||||
break; | break; | ||||
case 'X': | case 'X': | ||||
out.setf(std::ios::uppercase); | out.setf(std::ios::uppercase); | ||||
// FALLTHROUGH | // Falls through | ||||
case 'x': | case 'x': | ||||
case 'p': | case 'p': | ||||
out.setf(std::ios::hex, std::ios::basefield); | out.setf(std::ios::hex, std::ios::basefield); | ||||
intConversion = true; | intConversion = true; | ||||
break; | break; | ||||
case 'E': | case 'E': | ||||
out.setf(std::ios::uppercase); | out.setf(std::ios::uppercase); | ||||
// FALLTHROUGH | // Falls through | ||||
case 'e': | case 'e': | ||||
out.setf(std::ios::scientific, std::ios::floatfield); | out.setf(std::ios::scientific, std::ios::floatfield); | ||||
out.setf(std::ios::dec, std::ios::basefield); | out.setf(std::ios::dec, std::ios::basefield); | ||||
break; | break; | ||||
case 'F': | case 'F': | ||||
out.setf(std::ios::uppercase); | out.setf(std::ios::uppercase); | ||||
// FALLTHROUGH | // Falls through | ||||
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); | ||||
// FALLTHROUGH | // Falls through | ||||
case 'g': | case 'g': | ||||
out.setf(std::ios::dec, std::ios::basefield); | out.setf(std::ios::dec, std::ios::basefield); | ||||
// 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) ntrunc = static_cast<int>(out.precision()); | if (precisionSet) 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': | ||||
// Not supported - will cause problems! | // Not supported - will cause problems! | ||||
TINYFORMAT_ERROR( | TINYFORMAT_ERROR( | ||||
"tinyformat: %n conversion spec not supported"); | "tinyformat: %n conversion spec not supported"); | ||||
break; | break; | ||||
case '\0': | case '\0': | ||||
Show All 12 Lines | #endif | ||||
out.setf(std::ios::internal, std::ios::adjustfield); | out.setf(std::ios::internal, std::ios::adjustfield); | ||||
out.fill('0'); | out.fill('0'); | ||||
} | } | ||||
return c + 1; | return c + 1; | ||||
} | } | ||||
//------------------------------------------------------------------------------ | //------------------------------------------------------------------------------ | ||||
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 numArgs) { | ||||
int numFormatters) { | |||||
// Saved stream state | // Saved stream state | ||||
std::streamsize origWidth = out.width(); | std::streamsize origWidth = out.width(); | ||||
std::streamsize origPrecision = out.precision(); | std::streamsize origPrecision = out.precision(); | ||||
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 | ||||
// Parse the format string | // "%n$..." with `n` an integer. We detect this in | ||||
// `streamStateFromFormat`. | |||||
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 = | const char *fmtEnd = | ||||
streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, | streamStateFromFormat(out, positionalMode, spacePadPositive, | ||||
formatters, argIndex, numFormatters); | ntrunc, fmt, args, argIndex, numArgs); | ||||
if (argIndex >= numFormatters) { | // NB: argIndex may be incremented by reading variable | ||||
// Check args remain after reading any variable width/precision | // width/precision in `streamStateFromFormat`, so do the bounds | ||||
TINYFORMAT_ERROR("tinyformat: Not enough format arguments"); | // check here. | ||||
if (argIndex >= numArgs) { | |||||
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. | // between stream formatting and the printf() behaviour. | ||||
// Simulate it crudely by formatting into a temporary string | // Simulate it crudely by formatting into a temporary string | ||||
// stream and munging the resulting string. | // stream and munging the resulting string. | ||||
std::ostringstream tmpStream; | std::ostringstream tmpStream; | ||||
tmpStream.copyfmt(out); | tmpStream.copyfmt(out); | ||||
tmpStream.setf(std::ios::showpos); | tmpStream.setf(std::ios::showpos); | ||||
arg.format(tmpStream, fmt, fmtEnd, ntrunc); | arg.format(tmpStream, fmt, fmtEnd, ntrunc); | ||||
// allocates... yuck. | std::string result = tmpStream.str(); // allocates... yuck. | ||||
std::string result = tmpStream.str(); | 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); | ||||
out.flags(origFlags); | out.flags(origFlags); | ||||
out.fill(origFill); | out.fill(origFill); | ||||
} | } | ||||
} // namespace detail | } // namespace detail | ||||
/// List of template arguments format(), held in a type-opaque way. | /// List of template arguments format(), held in a type-opaque way. | ||||
/// | /// | ||||
/// A const reference to FormatList (typedef'd as FormatListRef) may be | /// A const reference to FormatList (typedef'd as FormatListRef) may be | ||||
/// conveniently used to pass arguments to non-template functions: All type | /// conveniently used to pass arguments to non-template functions: All type | ||||
/// information has been stripped from the arguments, leaving just enough of a | /// information has been stripped from the arguments, leaving just enough of a | ||||
/// common interface to perform formatting as required. | /// common interface to perform formatting as required. | ||||
class FormatList { | class FormatList { | ||||
public: | public: | ||||
FormatList(detail::FormatArg *formatters, int N) | FormatList(detail::FormatArg *args, int N) : m_args(args), m_N(N) {} | ||||
: m_formatters(formatters), 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; | ||||
}; | }; | ||||
/// Reference to type-opaque format list for passing to vformat() | /// Reference to type-opaque format list for passing to vformat() | ||||
typedef const FormatList &FormatListRef; | typedef const FormatList &FormatListRef; | ||||
namespace detail { | namespace detail { | ||||
// Format list subclass with fixed storage to avoid dynamic allocation | // Format list subclass with fixed storage to avoid dynamic allocation | ||||
template <int N> class FormatListN : public FormatList { | template <int N> 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), m_formatterStore{ | : FormatList(&m_formatterStore[0], N), m_formatterStore{ | ||||
FormatArg(args)...} { | FormatArg(args)...} { | ||||
static_assert(sizeof...(args) == N, "Number of args must be N"); | static_assert(sizeof...(args) == N, "Number of args must be N"); | ||||
} | } | ||||
#else // C++98 version | #else // C++98 version | ||||
void init(int) {} | void init(int) {} | ||||
#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) { \ | TINYFORMAT_ASSERT(n == N); \ | ||||
assert(n == N); \ | |||||
init(0, TINYFORMAT_PASSARGS(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)) { \ | ||||
m_formatterStore[i] = FormatArg(v1); \ | m_formatterStore[i] = FormatArg(v1); \ | ||||
init(i + 1 TINYFORMAT_PASSARGS_TAIL(n)); \ | init(i + 1 TINYFORMAT_PASSARGS_TAIL(n)); \ | ||||
} | } | ||||
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]; | ||||
}; | }; | ||||
// Special 0-arg version - MSVC says zero-sized C array in struct is | // Special 0-arg version - MSVC says zero-sized C array in struct is | ||||
// nonstandard. | // nonstandard. | ||||
template <> class FormatListN<0> : public FormatList { | template <> class FormatListN<0> : public FormatList { | ||||
Show All 34 Lines | |||||
#endif | #endif | ||||
/// Format list of arguments to the stream according to the given format string. | /// 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 | /// The name vformat() is chosen for the semantic similarity to vprintf(): the | ||||
/// 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); | ||||
} | } | ||||
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES | #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES | ||||
/// Format list of arguments to the stream according to given format string. | /// Format list of arguments to the stream according to given format string. | ||||
template <typename... Args> | template <typename... Args> | ||||
void format(std::ostream &out, const char *fmt, const Args &... args) { | void format(std::ostream &out, const char *fmt, const Args &... args) { | ||||
vformat(out, fmt, makeFormatList(args...)); | vformat(out, fmt, makeFormatList(args...)); | ||||
▲ Show 20 Lines • Show All 65 Lines • ▼ Show 20 Lines | void printfln(const char *fmt, TINYFORMAT_VARARGS(n)) { \ | ||||
std::cout << '\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 | // Added for Bitcoin | ||||
template <typename... Args> | template <typename... Args> | ||||
std::string format(const std::string &fmt, const Args &... args) { | std::string format(const std::string &fmt, const Args &... args) { | ||||
std::ostringstream oss; | std::ostringstream oss; | ||||
format(oss, fmt.c_str(), args...); | format(oss, fmt.c_str(), args...); | ||||
return oss.str(); | return oss.str(); | ||||
} | } | ||||
} // namespace tinyformat | } // namespace tinyformat | ||||
// Added for Bitcoin: | |||||
/** | /** | ||||
* Format arguments and return the string or write to given std::ostream (see | * Format arguments and return the string or write to given std::ostream (see | ||||
* tinyformat::format doc for details) | * tinyformat::format doc for details) | ||||
*/ | */ | ||||
#define strprintf tfm::format | #define strprintf tfm::format | ||||
#endif // TINYFORMAT_H_INCLUDED | #endif // TINYFORMAT_H_INCLUDED |