// tinyformat.h // Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com] // // Boost Software License - Version 1.0 // // Permission is hereby granted, free of charge, to any person or organization // obtaining a copy of the software and accompanying documentation covered by // this license (the "Software") to use, reproduce, display, distribute, // execute, and transmit the Software, and to prepare derivative works of the // Software, and to permit third-parties to whom the Software is furnished to // do so, all subject to the following: // // The copyright notices in the Software and this entire statement, including // the above license grant, this restriction and the following disclaimer, // must be included in all copies of the Software, in whole or in part, and // all derivative works of the Software, unless such copies or derivative // works are solely in the form of machine-executable object code generated by // a source language processor. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. //------------------------------------------------------------------------------ // Tinyformat: A minimal type safe printf replacement // // tinyformat.h is a type safe printf replacement library in a single C++ // header file. Design goals include: // // * Type safety and extensibility for user defined types. // * C99 printf() compatibility, to the extent possible using std::ostream // * Simplicity and minimalism. A single header file to include and distribute // with your projects. // * Augment rather than replace the standard stream formatting mechanism // * C++98 support, with optional C++11 niceties // // // Main interface example usage // ---------------------------- // // To print a date to std::cout: // // std::string weekday = "Wednesday"; // const char* month = "July"; // size_t day = 27; // long hour = 14; // int min = 44; // // tfm::printf("%s, %s %d, %.2d:%.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 // using either of the tfm::format() functions. One prints on a user provided // stream: // // tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // // The other returns a std::string: // // std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n", // weekday, month, day, hour, min); // std::cout << date; // // 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 // ----------------------------- // // Simulating variadic templates in C++98 is pretty painful since it requires // writing out the same function for each desired number of arguments. To make // this bearable tinyformat comes with a set of macros which are used // internally to generate the API, but which may also be used in user code. // // The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and // TINYFORMAT_PASSARGS(n) will generate a list of n argument types, // type/name pairs and argument names respectively when called with an integer // n between 1 and 16. We can use these to define a macro which generates the // desired user defined function with n arguments. To generate all 16 user // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM. For an // 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 // -------------------------- // // Error handling: Define TINYFORMAT_ERROR to customize the error handling for // format strings which are unsupported or have the wrong number of format // specifiers (calls assert() by default). // // User defined types: Uses operator<< for user defined types by default. // Overload formatValue() for more control. #pragma once #include #include #include #include namespace paddle { namespace string { namespace tinyformat { #ifndef TINYFORMAT_ERROR #define TINYFORMAT_ERROR(reason) assert(0 && reason) #endif //------------------------------------------------------------------------------ namespace detail { // Test whether type T1 is convertible to type T2 template struct is_convertible { private: // two types of different size struct fail { char dummy[2]; }; struct succeed { char dummy; }; // Try to convert a T1 to a T2 by plugging into tryConvert static fail tryConvert(...); static succeed tryConvert(const T2 &); static const T1 &makeT1(); public: // Standard trick: the (...) version of tryConvert will be chosen from // the overload set only if the version taking a T2 doesn't match. // Then we compare the sizes of the return types to check which // function matched. Very neat, in a disgusting kind of way :) static const bool value = sizeof(tryConvert(makeT1())) == sizeof(succeed); }; // Format the value by casting to type fmtT. This default implementation // should never be called. template ::value> struct formatValueAsType { static void invoke(std::ostream & /*out*/, const T & /*value*/) { assert(0); } }; // Specialized version for types that can actually be converted to fmtT, as // indicated by the "convertible" template parameter. template struct formatValueAsType { static void invoke(std::ostream &out, const T &value) { out << static_cast(value); } }; // Convert an arbitrary type to integer. The version with convertible=false // throws an error. template ::value> struct convertToInt { static int invoke(const T & /*value*/) { TINYFORMAT_ERROR( "tinyformat: Cannot convert from argument type to " "integer for use as variable width or precision"); return 0; } }; // Specialization for convertToInt when conversion is possible template struct convertToInt { static int invoke(const T &value) { return static_cast(value); } }; // Format at most ntrunc characters to the given stream. template 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(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 //------------------------------------------------------------------------------ // Variable formatting functions. May be overridden for user-defined types if // desired. /// Format a value into a stream, delegating to operator<< by default. /// /// 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 format specification is provided in the range [fmtBegin, fmtEnd). For /// truncating conversions, ntrunc is set to the desired maximum number of /// characters, for example "%.7s" calls formatValue with ntrunc = 7. /// /// 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 inline void formatValue(std::ostream &out, const char * /*fmtBegin*/, const char *fmtEnd, int ntrunc, const T &value) { // The mess here is to support the %c and %p conversions: if these // conversions are active we try to convert the type to a char or const // 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*). const bool canConvertToChar = detail::is_convertible::value; const bool canConvertToVoidPtr = detail::is_convertible::value; if (canConvertToChar && *(fmtEnd - 1) == 'c') detail::formatValueAsType::invoke(out, value); else if (canConvertToVoidPtr && *(fmtEnd - 1) == 'p') detail::formatValueAsType::invoke(out, value); 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 out << value; } // Overloaded version for char types to support printing as an integer #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType) \ inline void formatValue(std::ostream &out, \ const char * /*fmtBegin*/, \ const char *fmtEnd, \ int /**/, \ charType value) { \ switch (*(fmtEnd - 1)) { \ case 'u': \ case 'd': \ case 'i': \ case 'o': \ case 'X': \ case 'x': \ out << static_cast(value); \ break; \ default: \ out << value; \ break; \ } \ } // per 3.9.1: char, signed char and unsigned char are all distinct types TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char) TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char) #undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR //------------------------------------------------------------------------------ // Tools for emulating variadic templates in C++98. The basic idea here is // stolen from the boost preprocessor metaprogramming library and cut down to // be just general enough for what we need. #define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_##n #define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_##n #define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_##n #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 // 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` // (see http://nedbatchelder.com/code/cog). Alternatively you can just create // extra versions by hand. /*[[[cog maxParams = 16 def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1): for j in range(startInd,maxParams+1): list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)]) cog.outl(lineTemplate % {'j':j, 'list':list}) makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s', 'class T%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s', 'const T%(i)d& v%(i)d') cog.outl() makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d') cog.outl() cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1') makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s', 'v%(i)d', startInd = 2) cog.outl() cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n ' + ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)])) ]]]*/ #define TINYFORMAT_ARGTYPES_1 class T1 #define TINYFORMAT_ARGTYPES_2 class T1, class T2 #define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3 #define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4 #define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5 #define TINYFORMAT_ARGTYPES_6 \ class T1, class T2, class T3, class T4, class T5, class T6 #define TINYFORMAT_ARGTYPES_7 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7 #define TINYFORMAT_ARGTYPES_8 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8 #define TINYFORMAT_ARGTYPES_9 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9 #define TINYFORMAT_ARGTYPES_10 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10 #define TINYFORMAT_ARGTYPES_11 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11 #define TINYFORMAT_ARGTYPES_12 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11, class T12 #define TINYFORMAT_ARGTYPES_13 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11, class T12, class T13 #define TINYFORMAT_ARGTYPES_14 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11, class T12, class T13, \ class T14 #define TINYFORMAT_ARGTYPES_15 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11, class T12, class T13, \ class T14, class T15 #define TINYFORMAT_ARGTYPES_16 \ class T1, class T2, class T3, class T4, class T5, class T6, class T7, \ class T8, class T9, class T10, class T11, class T12, class T13, \ class T14, class T15, class T16 #define TINYFORMAT_VARARGS_1 const T1 &v1 #define TINYFORMAT_VARARGS_2 const T1 &v1, const T2 &v2 #define TINYFORMAT_VARARGS_3 const T1 &v1, const T2 &v2, const T3 &v3 #define TINYFORMAT_VARARGS_4 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4 #define TINYFORMAT_VARARGS_5 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5 #define TINYFORMAT_VARARGS_6 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6 #define TINYFORMAT_VARARGS_7 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7 #define TINYFORMAT_VARARGS_8 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8 #define TINYFORMAT_VARARGS_9 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9 #define TINYFORMAT_VARARGS_10 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10 #define TINYFORMAT_VARARGS_11 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11 #define TINYFORMAT_VARARGS_12 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11, const T12 &v12 #define TINYFORMAT_VARARGS_13 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11, const T12 &v12, const T13 &v13 #define TINYFORMAT_VARARGS_14 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14 #define TINYFORMAT_VARARGS_15 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14, \ const T15 &v15 #define TINYFORMAT_VARARGS_16 \ const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \ const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \ const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14, \ const T15 &v15, const T16 &v16 #define TINYFORMAT_PASSARGS_1 v1 #define TINYFORMAT_PASSARGS_2 v1, v2 #define TINYFORMAT_PASSARGS_3 v1, v2, v3 #define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4 #define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_13 \ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_14 \ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_15 \ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_16 \ v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #define TINYFORMAT_PASSARGS_TAIL_1 #define TINYFORMAT_PASSARGS_TAIL_2 , v2 #define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3 #define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4 #define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5 #define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6 #define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7 #define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8 #define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9 #define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10 #define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11 #define TINYFORMAT_PASSARGS_TAIL_12 \ , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12 #define TINYFORMAT_PASSARGS_TAIL_13 \ , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13 #define TINYFORMAT_PASSARGS_TAIL_14 \ , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14 #define TINYFORMAT_PASSARGS_TAIL_15 \ , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15 #define TINYFORMAT_PASSARGS_TAIL_16 \ , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16 #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(15) m(16) //[[[end]]] 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 // whereas a naive implementation based on inheritance does not. class FormatArg { public: FormatArg() {} template FormatArg(const T &value) : m_value(static_cast(&value)), m_formatImpl(&formatImpl), m_toIntImpl(&toIntImpl) {} void format(std::ostream &out, const char *fmtBegin, const char *fmtEnd, int ntrunc) const { m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value); } int toInt() const { return m_toIntImpl(m_value); } private: template static void formatImpl(std::ostream &out, const char *fmtBegin, const char *fmtEnd, int ntrunc, const void *value) { formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast(value)); } template static int toIntImpl(const void *value) { return convertToInt::invoke(*static_cast(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; 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. // // 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, c - fmt); return c; case '%': out.write(fmt, c - fmt); if (*(c + 1) != '%') return c; // for "%%", tack trailing % onto next literal section. fmt = ++c; break; default: break; } } } // 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". // // 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 // pointer to the character after the end of the current format spec. inline const char *streamStateFromFormat(std::ostream &out, bool &spacePadPositive, int &ntrunc, const char *fmtStart, const detail::FormatArg *formatters, int &argIndex, int numFormatters) { if (*fmtStart != '%') { TINYFORMAT_ERROR( "tinyformat: Not enough conversion specifiers in format string"); return fmtStart; } // Reset stream state to defaults. out.width(0); out.precision(6); out.fill(' '); // Reset most flags; ignore irrelevant unitbuf & skipws. out.unsetf(std::ios::adjustfield | std::ios::basefield | std::ios::floatfield | std::ios::showbase | std::ios::boolalpha | std::ios::showpoint | std::ios::showpos | std::ios::uppercase); bool precisionSet = false; 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; default: break; } break; } // 2) Parse width if (*c >= '0' && *c <= '9') { widthSet = true; out.width(parseIntAndAdvance(c)); } if (*c == '*') { widthSet = true; 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; } out.width(width); ++c; } // 3) Parse precision 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; } // 4) Ignore any C99 length modifier 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) { case 'u': case 'd': case 'i': out.setf(std::ios::dec, std::ios::basefield); intConversion = true; break; case 'o': out.setf(std::ios::oct, std::ios::basefield); intConversion = true; break; case 'X': out.setf(std::ios::uppercase); case 'x': case 'p': out.setf(std::ios::hex, std::ios::basefield); intConversion = true; break; case 'E': out.setf(std::ios::uppercase); 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); case 'f': out.setf(std::ios::fixed, std::ios::floatfield); break; case 'G': out.setf(std::ios::uppercase); 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) ntrunc = static_cast(out.precision()); // Make %s print booleans as "true" and "false" out.setf(std::ios::boolalpha); break; case 'n': // Not supported - will cause problems! TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported"); break; case '\0': TINYFORMAT_ERROR( "tinyformat: Conversion spec incorrectly " "terminated by end of string"); return c; default: break; } 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 // the width isn't otherwise used. out.width(out.precision() + widthExtra); out.setf(std::ios::internal, std::ios::adjustfield); out.fill('0'); } return c + 1; } //------------------------------------------------------------------------------ inline void formatImpl(std::ostream &out, const char *fmt, const detail::FormatArg *formatters, int numFormatters) { // 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 class FormatListN : public FormatList { public: template FormatListN(const Args &... args) : FormatList(&m_formatterStore[0], N), m_formatterStore{FormatArg(args)...} { static_assert(sizeof...(args) == N, "Number of args must be N"); } 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 { public: FormatListN() : FormatList(0, 0) {} }; } // namespace detail //------------------------------------------------------------------------------ // Primary API functions /// Make type-agnostic format list from list of template arguments. /// /// 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 detail::FormatListN makeFormatList(const Args &... args) { return detail::FormatListN(args...); } /// 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); } /// Format list of arguments to the stream according to given format string. template 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 std::string format(const char *fmt, const Args &... args) { std::ostringstream oss; format(oss, fmt, args...); return oss.str(); } /// Format list of arguments to std::cout, according to the given format string template void printf(const char *fmt, const Args &... args) { format(std::cout, fmt, args...); } template void printfln(const char *fmt, const Args &... args) { format(std::cout, fmt, args...); std::cout << '\n'; } } // namespace tinyformat } // namespace string } // namespace paddle