diff options
author | LDA <lda@ldasuku.net> | 2023-06-26 11:48:27 -0700 |
---|---|---|
committer | LDA <lda@ldasuku.net> | 2023-06-26 11:58:35 -0700 |
commit | c9e889723b7af832322d178975e6e440d6cd3ae5 (patch) | |
tree | a8429c30ecf46ed1388df13b614141e73973cd9f /source/extern/tinyformat.h | |
parent | 4585c9cafa87cad6b54397af7e9375cc31b72f89 (diff) |
resolved most of the compiler warnings/errors under gcc
also update xxhash and tinyformat
Diffstat (limited to 'source/extern/tinyformat.h')
-rw-r--r-- | source/extern/tinyformat.h | 512 |
1 files changed, 311 insertions, 201 deletions
diff --git a/source/extern/tinyformat.h b/source/extern/tinyformat.h index 6aa00a7..b588bf9 100644 --- a/source/extern/tinyformat.h +++ b/source/extern/tinyformat.h @@ -33,6 +33,7 @@ // // * Type safety and extensibility for user defined types. // * 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 // with your projects. // * Augment rather than replace the standard stream formatting mechanism @@ -42,7 +43,7 @@ // 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"; // const char* month = "July"; @@ -52,6 +53,14 @@ // // 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 // possible to print a std::string using the "%s" conversion, and a // size_t using the "%d" conversion. A similar result could be achieved @@ -133,11 +142,17 @@ namespace tfm = tinyformat; //------------------------------------------------------------------------------ // Implementation details. #include <algorithm> -#include <cassert> #include <iostream> #include <sstream> +#include <cmath> + +#ifndef TINYFORMAT_ASSERT +# include <cassert> +# define TINYFORMAT_ASSERT(cond) assert(cond) +#endif #ifndef TINYFORMAT_ERROR +# include <cassert> # define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif @@ -147,23 +162,14 @@ namespace tfm = tinyformat; # endif #endif -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES -# include <array> -# if defined(_MSC_VER) && _MSC_VER <= 1800 // VS2013 -# define TINYFORMAT_BRACED_INIT_WORKAROUND(x) (x) -# else -# define TINYFORMAT_BRACED_INIT_WORKAROUND(x) {x} -# endif -#endif - #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 # define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND #endif #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) # define TINYFORMAT_HIDDEN __attribute__((visibility("hidden"))) #else @@ -219,7 +225,7 @@ template<int n> struct is_wchar<wchar_t[n]> {}; template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value> 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 // indicated by the "convertible" template parameter. @@ -241,8 +247,7 @@ struct formatZeroIntegerWorkaround<T,true> { 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"; return true; } @@ -277,13 +282,13 @@ 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()))); + 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) \ + while (len < ntrunc && value[len] != 0) \ ++len; \ out.write(value, len); \ } @@ -293,6 +298,21 @@ TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char) TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char) #undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR +template<typename T> +void spaceFillIfNotFinite(std::ostream& out, const T& value) { } +// TODO: type_traits would clearly be better here. Should consider moving all +// these workarounds into a big pre-C++11 section. +#define TINYFORMAT_SETFILL_NOT_FINITE_FLOATING(type) \ +inline void spaceFillIfNotFinite(std::ostream& out, type value) \ +{ \ + if (out.fill() == '0' && !std::isfinite(value)) \ + out.fill(' '); \ +} +TINYFORMAT_SETFILL_NOT_FINITE_FLOATING(float) +TINYFORMAT_SETFILL_NOT_FINITE_FLOATING(double) +TINYFORMAT_SETFILL_NOT_FINITE_FLOATING(long double) +#undef TINYFORMAT_SETFILL_NOT_FINITE_FLOATING + } // namespace detail @@ -327,17 +347,17 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, // void* respectively and format that instead of the value itself. For the // %p conversion it's important to avoid dereferencing the pointer, which // could otherwise lead to a crash when printing a dangling (const char*). - bool canConvertToChar = detail::is_convertible<T,char>::value; - bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value; - if(canConvertToChar && *(fmtEnd-1) == 'c') + const bool canConvertToChar = detail::is_convertible<T,char>::value; + const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value; + detail::spaceFillIfNotFinite(out, value); + if (canConvertToChar && *(fmtEnd-1) == 'c') detail::formatValueAsType<T, char>::invoke(out, value); - else if(canConvertToVoidPtr && *(fmtEnd-1) == 'p') + else if (canConvertToVoidPtr && *(fmtEnd-1) == 'p') detail::formatValueAsType<T, const void*>::invoke(out, value); #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND - else if(detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; + else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/; #endif - else if(ntrunc >= 0) - { + 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); @@ -352,8 +372,7 @@ inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, inline void formatValue(std::ostream& out, const char* /*fmtBegin*/, \ const char* fmtEnd, int /**/, charType value) \ { \ - switch(*(fmtEnd-1)) \ - { \ + switch (*(fmtEnd-1)) { \ case 'u': case 'd': case 'i': case 'o': case 'X': case 'x': \ out << static_cast<int>(value); break; \ default: \ @@ -378,11 +397,11 @@ TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) #define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n // To keep it as transparent as possible, the macros below have been generated -// using python via the excellent cog.py code generation script. This avoids +// using python via the excellent cog code generation script. This avoids // the need for a bunch of complex (but more general) preprocessor tricks as // used in boost.preprocessor. // -// To rerun the code generation in place, use `cog.py -r tinyformat.h` +// To rerun the code generation in place, use `cog -r tinyformat.h` // (see http://nedbatchelder.com/code/cog). Alternatively you can just create // extra versions by hand. @@ -491,16 +510,22 @@ namespace detail { // Type-opaque holder for an argument to format(), with associated actions on // 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. class FormatArg { public: - FormatArg() {} + FormatArg() + : m_value(NULL), + m_formatImpl(NULL), + m_toIntImpl(NULL) + { } template<typename T> FormatArg(const T& value) - : m_value(static_cast<const void*>(&value)), + // C-style cast here allows us to also remove volatile; we put it + // back in the *Impl functions before dereferencing to avoid UB. + : m_value((const void*)(&value)), m_formatImpl(&formatImpl<T>), m_toIntImpl(&toIntImpl<T>) { } @@ -508,11 +533,15 @@ class FormatArg void format(std::ostream& out, const char* fmtBegin, const char* fmtEnd, int ntrunc) const { + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_formatImpl); m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { + TINYFORMAT_ASSERT(m_value); + TINYFORMAT_ASSERT(m_toIntImpl); return m_toIntImpl(m_value); } @@ -542,34 +571,68 @@ class FormatArg inline int parseIntAndAdvance(const char*& c) { int i = 0; - for(;*c >= '0' && *c <= '9'; ++c) + for (;*c >= '0' && *c <= '9'; ++c) i = 10*i + (*c - '0'); return i; } -// Print literal part of format string and return next format spec -// position. +// 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. // -// 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. +// 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. inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) { const char* c = fmt; - for(;; ++c) - { - switch(*c) - { - case '\0': - out.write(fmt, static_cast<std::streamsize>(c - fmt)); + for (;; ++c) { + if (*c == '\0') { + out.write(fmt, c - fmt); + return c; + } + else if (*c == '%') { + out.write(fmt, c - fmt); + if (*(c+1) != '%') return c; - case '%': - out.write(fmt, static_cast<std::streamsize>(c - fmt)); - if(*(c+1) != '%') - return c; - // for "%%", tack trailing % onto next literal section. - fmt = ++c; - break; + // for "%%", tack trailing % onto next literal section. + fmt = ++c; } } } @@ -578,23 +641,43 @@ inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt) // Parse a format string and set the stream state accordingly. // // 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 // state are returned in spacePadPositive (for space padded positive numbers) // 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. -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, - const detail::FormatArg* formatters, - int& argIndex, int numFormatters) + const detail::FormatArg* args, + int& argIndex, int numArgs) { - if(*fmtStart != '%') - { - TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string"); - return fmtStart; - } + TINYFORMAT_ASSERT(*fmtStart == '%'); // Reset stream state to defaults. out.width(0); out.precision(6); @@ -607,98 +690,113 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi bool widthSet = false; int widthExtra = 0; const char* c = fmtStart + 1; - // 1) Parse flags - for(;; ++c) - { - switch(*c) - { - case '#': - out.setf(std::ios::showpoint | std::ios::showbase); - continue; - case '0': - // overridden by left alignment ('-' flag) - if(!(out.flags() & std::ios::left)) - { - // 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); - } - continue; - case '-': - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - continue; - case ' ': - // overridden by show positive sign, '+' flag. - if(!(out.flags() & std::ios::showpos)) - spacePadPositive = true; - continue; - case '+': - out.setf(std::ios::showpos); - spacePadPositive = false; - widthExtra = 1; - continue; + + // 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); + } } - break; } - // 2) Parse width - if(*c >= '0' && *c <= '9') - { - widthSet = true; - out.width(parseIntAndAdvance(c)); + else if (positionalMode) { + TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one"); } - if(*c == '*') - { - widthSet = true; + // 2) Parse flags and width if we did not do it in previous step. + if (!widthSet) { + // Parse flags + for (;; ++c) { + switch (*c) { + case '#': + out.setf(std::ios::showpoint | std::ios::showbase); + continue; + case '0': + // overridden by left alignment ('-' flag) + if (!(out.flags() & std::ios::left)) { + // 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); + } + continue; + case '-': + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + continue; + case ' ': + // overridden by show positive sign, '+' flag. + if (!(out.flags() & std::ios::showpos)) + spacePadPositive = true; + continue; + case '+': + out.setf(std::ios::showpos); + spacePadPositive = false; + widthExtra = 1; + continue; + default: + break; + } + break; + } + // Parse width 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 - out.fill(' '); - out.setf(std::ios::left, std::ios::adjustfield); - width = -width; + widthSet = parseWidthOrPrecision(width, c, positionalMode, + args, argIndex, numArgs); + if (widthSet) { + if (width < 0) { + // negative widths correspond to '-' flag set + out.fill(' '); + out.setf(std::ios::left, std::ios::adjustfield); + width = -width; + } + out.width(width); } - out.width(width); - ++c; } // 3) Parse precision - if(*c == '.') - { + if (*c == '.') { ++c; int precision = 0; - if(*c == '*') - { - ++c; - if(argIndex < numFormatters) - precision = formatters[argIndex++].toInt(); - else - 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); - precisionSet = true; + parseWidthOrPrecision(precision, c, positionalMode, + args, argIndex, numArgs); + // Presence of `.` indicates precision set, unless the inferred value + // was negative in which case the default is used. + precisionSet = precision >= 0; + if (precisionSet) + out.precision(precision); } // 4) Ignore any C99 length modifier - while(*c == 'l' || *c == 'h' || *c == 'L' || - *c == 'j' || *c == 'z' || *c == 't') + while (*c == 'l' || *c == 'h' || *c == 'L' || + *c == 'j' || *c == 'z' || *c == 't') { ++c; + } // 5) We're up to the conversion specifier character. // Set stream flags based on conversion specifier (thanks to the // boost::format class for forging the way here). bool intConversion = false; - switch(*c) - { + switch (*c) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; @@ -709,39 +807,51 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi break; case 'X': out.setf(std::ios::uppercase); + // Falls through case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); + // Falls through case 'e': out.setf(std::ios::scientific, std::ios::floatfield); out.setf(std::ios::dec, std::ios::basefield); break; case 'F': out.setf(std::ios::uppercase); + // Falls through case 'f': out.setf(std::ios::fixed, std::ios::floatfield); 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': out.setf(std::ios::uppercase); + // Falls through case 'g': out.setf(std::ios::dec, std::ios::basefield); // As in boost::format, let stream decide float format. out.flags(out.flags() & ~std::ios::floatfield); break; - case 'a': case 'A': - TINYFORMAT_ERROR("tinyformat: the %a and %A conversion specs " - "are not supported"); - break; case 'c': // Handled as special case inside formatValue() break; case 's': - if(precisionSet) + 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); break; case 'n': @@ -752,9 +862,10 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly " "terminated by end of string"); return c; + default: + break; } - if(intConversion && precisionSet && !widthSet) - { + if (intConversion && precisionSet && !widthSet) { // "precision" for integers gives the minimum number of digits (to be // padded with zeros on the left). This isn't really supported by the // iostreams, but we can approximately simulate it with the width if @@ -769,8 +880,8 @@ inline const char* streamStateFromFormat(std::ostream& out, bool& spacePadPositi //------------------------------------------------------------------------------ inline void formatImpl(std::ostream& out, const char* fmt, - const detail::FormatArg* formatters, - int numFormatters) + const detail::FormatArg* args, + int numArgs) { // Saved stream state std::streamsize origWidth = out.width(); @@ -778,26 +889,34 @@ inline void formatImpl(std::ostream& out, const char* fmt, std::ios::fmtflags origFlags = out.flags(); char origFill = out.fill(); - for (int argIndex = 0; argIndex < numFormatters; ++argIndex) - { - // Parse the format string + // "Positional mode" means all format specs should be of the form "%n$..." + // with `n` an integer. We detect this in `streamStateFromFormat`. + bool positionalMode = false; + int argIndex = 0; + while (true) { 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; 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"); + const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt, + args, argIndex, numArgs); + // NB: argIndex may be incremented by reading variable width/precision + // in `streamStateFromFormat`, so do the bounds check here. + if (argIndex >= numArgs) { + TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string"); return; } - const FormatArg& arg = formatters[argIndex]; + const FormatArg& arg = args[argIndex]; // Format the arg into the stream. - if(!spacePadPositive) + if (!spacePadPositive) { arg.format(out, fmt, fmtEnd, ntrunc); - else - { + } + 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 @@ -807,18 +926,17 @@ inline void formatImpl(std::ostream& out, const char* fmt, 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] = ' '; + for (size_t i = 0, iend = result.size(); i < iend; ++i) { + if (result[i] == '+') + result[i] = ' '; + } out << result; } + if (!positionalMode) + ++argIndex; 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); @@ -838,17 +956,14 @@ inline void formatImpl(std::ostream& out, const char* fmt, class FormatList { public: + FormatList(detail::FormatArg* args, int N) + : m_args(args), m_N(N) { } + friend void vformat(std::ostream& out, const char* fmt, const FormatList& list); - protected: - void setFormatters(detail::FormatArg* formatters, int N) { - m_formatters = formatters; - m_N = N; - } - private: - const detail::FormatArg* m_formatters; + const detail::FormatArg* m_args; int m_N; }; @@ -859,53 +974,48 @@ typedef const FormatList& FormatListRef; namespace detail { // Format list subclass with fixed storage to avoid dynamic allocation -template<int N> +template<std::size_t N> class FormatListN : public FormatList { public: #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES template<typename... Args> FormatListN(const Args&... args) - : m_formatterStore TINYFORMAT_BRACED_INIT_WORKAROUND({ FormatArg(args)... }) - { - static_assert(sizeof...(args) == N, "Number of args must be N"); - setFormatters(&m_formatterStore[0], N); - } + : 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)) \ - { \ - setFormatters(&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)); \ +# define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n) \ + \ + template<TINYFORMAT_ARGTYPES(n)> \ + FormatListN(TINYFORMAT_VARARGS(n)) \ + : FormatList(&m_formatterStore[0], n) \ + { TINYFORMAT_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)); \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR) # undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR #endif + FormatListN(const FormatListN& other) + : FormatList(&m_formatterStore[0], N) + { std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N], + &m_formatterStore[0]); } private: -#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES - std::array<FormatArg, N> m_formatterStore; -#else // C++98 version FormatArg m_formatterStore[N]; -#endif }; // Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard template<> class FormatListN<0> : public FormatList { - public: FormatListN() { setFormatters(0, 0); } + public: FormatListN() : FormatList(0, 0) {} }; } // namespace detail @@ -951,7 +1061,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST) /// 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); + detail::formatImpl(out, fmt, list.m_args, list.m_N); } |