tinyformat.h raw

   1  // tinyformat.h
   2  // Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com]
   3  //
   4  // Boost Software License - Version 1.0
   5  //
   6  // Permission is hereby granted, free of charge, to any person or organization
   7  // obtaining a copy of the software and accompanying documentation covered by
   8  // this license (the "Software") to use, reproduce, display, distribute,
   9  // execute, and transmit the Software, and to prepare derivative works of the
  10  // Software, and to permit third-parties to whom the Software is furnished to
  11  // do so, all subject to the following:
  12  //
  13  // The copyright notices in the Software and this entire statement, including
  14  // the above license grant, this restriction and the following disclaimer,
  15  // must be included in all copies of the Software, in whole or in part, and
  16  // all derivative works of the Software, unless such copies or derivative
  17  // works are solely in the form of machine-executable object code generated by
  18  // a source language processor.
  19  //
  20  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  21  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  22  // FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
  23  // SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
  24  // FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
  25  // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  26  // DEALINGS IN THE SOFTWARE.
  27  
  28  //------------------------------------------------------------------------------
  29  // Tinyformat: A minimal type safe printf replacement
  30  //
  31  // tinyformat.h is a type safe printf replacement library in a single C++
  32  // header file.  Design goals include:
  33  //
  34  // * Type safety and extensibility for user defined types.
  35  // * C99 printf() compatibility, to the extent possible using std::ostream
  36  // * POSIX extension for positional arguments
  37  // * Simplicity and minimalism.  A single header file to include and distribute
  38  //   with your projects.
  39  // * Augment rather than replace the standard stream formatting mechanism
  40  // * C++98 support, with optional C++11 niceties
  41  //
  42  //
  43  // Main interface example usage
  44  // ----------------------------
  45  //
  46  // To print a date to std::cout for American usage:
  47  //
  48  //   std::string weekday = "Wednesday";
  49  //   const char* month = "July";
  50  //   size_t day = 27;
  51  //   long hour = 14;
  52  //   int min = 44;
  53  //
  54  //   tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
  55  //
  56  // POSIX extension for positional arguments is available.
  57  // The ability to rearrange formatting arguments is an important feature
  58  // for localization because the word order may vary in different languages.
  59  //
  60  // Previous example for German usage. Arguments are reordered:
  61  //
  62  //   tfm::printf("%1$s, %3$d. %2$s, %4$d:%5$.2d\n", weekday, month, day, hour, min);
  63  //
  64  // The strange types here emphasize the type safety of the interface; it is
  65  // possible to print a std::string using the "%s" conversion, and a
  66  // size_t using the "%d" conversion.  A similar result could be achieved
  67  // using either of the tfm::format() functions.  One prints on a user provided
  68  // stream:
  69  //
  70  //   tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
  71  //               weekday, month, day, hour, min);
  72  //
  73  // The other returns a std::string:
  74  //
  75  //   std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
  76  //                                  weekday, month, day, hour, min);
  77  //   std::cout << date;
  78  //
  79  // These are the three primary interface functions.  There is also a
  80  // convenience function printfln() which appends a newline to the usual result
  81  // of printf() for super simple logging.
  82  //
  83  //
  84  // User defined format functions
  85  // -----------------------------
  86  //
  87  // Simulating variadic templates in C++98 is pretty painful since it requires
  88  // writing out the same function for each desired number of arguments.  To make
  89  // this bearable tinyformat comes with a set of macros which are used
  90  // internally to generate the API, but which may also be used in user code.
  91  //
  92  // The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and
  93  // TINYFORMAT_PASSARGS(n) will generate a list of n argument types,
  94  // type/name pairs and argument names respectively when called with an integer
  95  // n between 1 and 16.  We can use these to define a macro which generates the
  96  // desired user defined function with n arguments.  To generate all 16 user
  97  // defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM.  For an
  98  // example, see the implementation of printf() at the end of the source file.
  99  //
 100  // Sometimes it's useful to be able to pass a list of format arguments through
 101  // to a non-template function.  The FormatList class is provided as a way to do
 102  // this by storing the argument list in a type-opaque way.  Continuing the
 103  // example from above, we construct a FormatList using makeFormatList():
 104  //
 105  //   FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour, min);
 106  //
 107  // The format list can now be passed into any non-template function and used
 108  // via a call to the vformat() function:
 109  //
 110  //   tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
 111  //
 112  //
 113  // Additional API information
 114  // --------------------------
 115  //
 116  // Error handling: Define TINYFORMAT_ERROR to customize the error handling for
 117  // format strings which are unsupported or have the wrong number of format
 118  // specifiers (calls assert() by default).
 119  //
 120  // User defined types: Uses operator<< for user defined types by default.
 121  // Overload formatValue() for more control.
 122  
 123  
 124  #ifndef TINYFORMAT_H_INCLUDED
 125  #define TINYFORMAT_H_INCLUDED
 126  
 127  namespace tinyformat {}
 128  //------------------------------------------------------------------------------
 129  // Config section.  Customize to your liking!
 130  
 131  // Namespace alias to encourage brevity
 132  namespace tfm = tinyformat;
 133  
 134  // Error handling; calls assert() by default.
 135  #define TINYFORMAT_ERROR(reasonString) throw tinyformat::format_error(reasonString)
 136  
 137  // Define for C++11 variadic templates which make the code shorter & more
 138  // general.  If you don't define this, C++11 support is autodetected below.
 139  #define TINYFORMAT_USE_VARIADIC_TEMPLATES
 140  
 141  
 142  //------------------------------------------------------------------------------
 143  // Implementation details.
 144  #include <algorithm>
 145  #include <attributes.h> // Added for Limenka
 146  #include <iostream>
 147  #include <sstream>
 148  #include <stdexcept> // Added for Limenka
 149  #include <util/string.h> // Added for Limenka
 150  
 151  #ifndef TINYFORMAT_ASSERT
 152  #   include <cassert>
 153  #   define TINYFORMAT_ASSERT(cond) assert(cond)
 154  #endif
 155  
 156  #ifndef TINYFORMAT_ERROR
 157  #   include <cassert>
 158  #   define TINYFORMAT_ERROR(reason) assert(0 && reason)
 159  #endif
 160  
 161  #if !defined(TINYFORMAT_USE_VARIADIC_TEMPLATES) && !defined(TINYFORMAT_NO_VARIADIC_TEMPLATES)
 162  #   ifdef __GXX_EXPERIMENTAL_CXX0X__
 163  #       define TINYFORMAT_USE_VARIADIC_TEMPLATES
 164  #   endif
 165  #endif
 166  
 167  #if defined(__GLIBCXX__) && __GLIBCXX__ < 20080201
 168  //  std::showpos is broken on old libstdc++ as provided with macOS.  See
 169  //  http://gcc.gnu.org/ml/libstdc++/2007-11/msg00075.html
 170  #   define TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
 171  #endif
 172  
 173  #ifdef __APPLE__
 174  // Workaround macOS linker warning: Xcode uses different default symbol
 175  // visibilities for static libs vs executables (see issue #25)
 176  #   define TINYFORMAT_HIDDEN __attribute__((visibility("hidden")))
 177  #else
 178  #   define TINYFORMAT_HIDDEN
 179  #endif
 180  
 181  namespace tinyformat {
 182  
 183  // Added for Limenka. Similar to std::runtime_format from C++26.
 184  struct RuntimeFormat {
 185      const std::string& fmt; // Not a string view, because tinyformat requires a c_str
 186      explicit RuntimeFormat(LIFETIMEBOUND const std::string& str) : fmt{str} {}
 187  };
 188  
 189  // Added for Limenka. Wrapper for checking format strings at compile time.
 190  // Unlike ConstevalFormatString this supports RunTimeFormat-wrapped std::string
 191  // for runtime string formatting without compile time checks.
 192  template <unsigned num_params>
 193  struct FormatStringCheck {
 194      consteval FormatStringCheck(const char* str) : fmt{util::ConstevalFormatString<num_params>{str}.fmt} {}
 195      FormatStringCheck(LIFETIMEBOUND const RuntimeFormat& run) : fmt{run.fmt.c_str()} {}
 196      FormatStringCheck(util::ConstevalFormatString<num_params> str) : fmt{str.fmt} {}
 197      operator const char*() { return fmt; }
 198      const char* fmt;
 199  };
 200  
 201  // Added for Limenka
 202  class format_error: public std::runtime_error
 203  {
 204  public:
 205      explicit format_error(const std::string &what): std::runtime_error(what) {
 206      }
 207  };
 208  
 209  // __int128 formatter used by formatTruncated and formatValue.
 210  // In namespace tinyformat (not detail) so both templates find it.
 211  inline void formatInt128(std::ostream& out, __int128 value) {
 212      __int128 abs_n = value;
 213      if (abs_n < 0) { out << char('-'); abs_n = -abs_n; }
 214      char buf[40]; int p = sizeof(buf);
 215      do { buf[--p] = '0' + (int)(abs_n % 10); abs_n /= 10; } while (abs_n > 0);
 216      out.write(buf + p, sizeof(buf) - p);
 217  }
 218  
 219  //------------------------------------------------------------------------------
 220  namespace detail {
 221  
 222  // Test whether type T1 is convertible to type T2
 223  template <typename T1, typename T2>
 224  struct is_convertible
 225  {
 226      private:
 227          // two types of different size
 228          struct fail { char dummy[2]; };
 229          struct succeed { char dummy; };
 230          // Try to convert a T1 to a T2 by plugging into tryConvert
 231          static fail tryConvert(...);
 232          static succeed tryConvert(const T2&);
 233          static const T1& makeT1();
 234      public:
 235  #       ifdef _MSC_VER
 236          // Disable spurious loss of precision warnings in tryConvert(makeT1())
 237  #       pragma warning(push)
 238  #       pragma warning(disable:4244)
 239  #       pragma warning(disable:4267)
 240  #       endif
 241          // Standard trick: the (...) version of tryConvert will be chosen from
 242          // the overload set only if the version taking a T2 doesn't match.
 243          // Then we compare the sizes of the return types to check which
 244          // function matched.  Very neat, in a disgusting kind of way :)
 245          static const bool value =
 246              sizeof(tryConvert(makeT1())) == sizeof(succeed);
 247  #       ifdef _MSC_VER
 248  #       pragma warning(pop)
 249  #       endif
 250  };
 251  
 252  
 253  // Detect when a type is not a wchar_t string
 254  template<typename T> struct is_wchar { typedef int tinyformat_wchar_is_not_supported; };
 255  template<> struct is_wchar<wchar_t*> {};
 256  template<> struct is_wchar<const wchar_t*> {};
 257  template<int n> struct is_wchar<const wchar_t[n]> {};
 258  template<int n> struct is_wchar<wchar_t[n]> {};
 259  
 260  
 261  // Format the value by casting to type fmtT.  This default implementation
 262  // should never be called.
 263  template<typename T, typename fmtT, bool convertible = is_convertible<T, fmtT>::value>
 264  struct formatValueAsType
 265  {
 266      static void invoke(std::ostream& /*out*/, const T& /*value*/) { TINYFORMAT_ASSERT(0); }
 267  };
 268  // Specialized version for types that can actually be converted to fmtT, as
 269  // indicated by the "convertible" template parameter.
 270  template<typename T, typename fmtT>
 271  struct formatValueAsType<T,fmtT,true>
 272  {
 273      static void invoke(std::ostream& out, const T& value)
 274          { out << static_cast<fmtT>(value); }
 275  };
 276  
 277  #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
 278  template<typename T, bool convertible = is_convertible<T, int>::value>
 279  struct formatZeroIntegerWorkaround
 280  {
 281      static bool invoke(std::ostream& /**/, const T& /**/) { return false; }
 282  };
 283  template<typename T>
 284  struct formatZeroIntegerWorkaround<T,true>
 285  {
 286      static bool invoke(std::ostream& out, const T& value)
 287      {
 288          if (static_cast<int>(value) == 0 && out.flags() & std::ios::showpos) {
 289              out << "+0";
 290              return true;
 291          }
 292          return false;
 293      }
 294  };
 295  #endif // TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
 296  
 297  // Convert an arbitrary type to integer.  The version with convertible=false
 298  // throws an error.
 299  template<typename T, bool convertible = is_convertible<T,int>::value>
 300  struct convertToInt
 301  {
 302      static int invoke(const T& /*value*/)
 303      {
 304          TINYFORMAT_ERROR("tinyformat: Cannot convert from argument type to "
 305                           "integer for use as variable width or precision");
 306          return 0;
 307      }
 308  };
 309  // Specialization for convertToInt when conversion is possible
 310  template<typename T>
 311  struct convertToInt<T,true>
 312  {
 313      static int invoke(const T& value) { return static_cast<int>(value); }
 314  };
 315  
 316  // Format at most ntrunc characters to the given stream.
 317  template<typename T>
 318  inline void formatTruncated(std::ostream& out, const T& value, int ntrunc)
 319  {
 320      std::ostringstream tmp;
 321      if constexpr (std::is_same_v<T, __int128>) { formatInt128(tmp, value); }
 322      else { tmp << value; }
 323      std::string result = tmp.str();
 324      out.write(result.c_str(), (std::min)(ntrunc, static_cast<int>(result.size())));
 325  }
 326  #define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type)       \
 327  inline void formatTruncated(std::ostream& out, type* value, int ntrunc) \
 328  {                                                           \
 329      std::streamsize len = 0;                                \
 330      while (len < ntrunc && value[len] != 0)                 \
 331          ++len;                                              \
 332      out.write(value, len);                                  \
 333  }
 334  // Overload for const char* and char*.  Could overload for signed & unsigned
 335  // char too, but these are technically unneeded for printf compatibility.
 336  TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
 337  TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
 338  #undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR
 339  
 340  } // namespace detail
 341  
 342  
 343  //------------------------------------------------------------------------------
 344  // Variable formatting functions.  May be overridden for user-defined types if
 345  // desired.
 346  
 347  
 348  /// Format a value into a stream, delegating to operator<< by default.
 349  ///
 350  /// Users may override this for their own types.  When this function is called,
 351  /// the stream flags will have been modified according to the format string.
 352  /// The format specification is provided in the range [fmtBegin, fmtEnd).  For
 353  /// truncating conversions, ntrunc is set to the desired maximum number of
 354  /// characters, for example "%.7s" calls formatValue with ntrunc = 7.
 355  ///
 356  /// By default, formatValue() uses the usual stream insertion operator
 357  /// operator<< to format the type T, with special cases for the %c and %p
 358  /// conversions.
 359  template<typename T>
 360  inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,
 361                          const char* fmtEnd, int ntrunc, const T& value)
 362  {
 363  #ifndef TINYFORMAT_ALLOW_WCHAR_STRINGS
 364      // Since we don't support printing of wchar_t using "%ls", make it fail at
 365      // compile time in preference to printing as a void* at runtime.
 366      typedef typename detail::is_wchar<T>::tinyformat_wchar_is_not_supported DummyType;
 367      (void) DummyType(); // avoid unused type warning with gcc-4.8
 368  #endif
 369      // The mess here is to support the %c and %p conversions: if these
 370      // conversions are active we try to convert the type to a char or const
 371      // void* respectively and format that instead of the value itself.  For the
 372      // %p conversion it's important to avoid dereferencing the pointer, which
 373      // could otherwise lead to a crash when printing a dangling (const char*).
 374      const bool canConvertToChar = detail::is_convertible<T,char>::value;
 375      const bool canConvertToVoidPtr = detail::is_convertible<T, const void*>::value;
 376      if (canConvertToChar && *(fmtEnd-1) == 'c')
 377          detail::formatValueAsType<T, char>::invoke(out, value);
 378      else if (canConvertToVoidPtr && *(fmtEnd-1) == 'p')
 379          detail::formatValueAsType<T, const void*>::invoke(out, value);
 380  #ifdef TINYFORMAT_OLD_LIBSTDCPLUSPLUS_WORKAROUND
 381      else if (detail::formatZeroIntegerWorkaround<T>::invoke(out, value)) /**/;
 382  #endif
 383      else if (ntrunc >= 0) {
 384          // Take care not to overread C strings in truncating conversions like
 385          // "%.4s" where at most 4 characters may be read.
 386          detail::formatTruncated(out, value, ntrunc);
 387      }
 388      else if constexpr (std::is_same_v<T, __int128>) { formatInt128(out, value); }
 389      else { out << value; }
 390  }
 391  
 392  
 393  // Overloaded version for char types to support printing as an integer
 394  #define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType)                  \
 395  inline void formatValue(std::ostream& out, const char* /*fmtBegin*/,  \
 396                          const char* fmtEnd, int /**/, charType value) \
 397  {                                                                     \
 398      switch (*(fmtEnd-1)) {                                            \
 399          case 'u': case 'd': case 'i': case 'o': case 'X': case 'x':   \
 400              out << static_cast<int>(value); break;                    \
 401          default:                                                      \
 402              out << value;                   break;                    \
 403      }                                                                 \
 404  }
 405  // per 3.9.1: char, signed char and unsigned char are all distinct types
 406  TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char)
 407  TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char)
 408  TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char)
 409  #undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR
 410  
 411  
 412  //------------------------------------------------------------------------------
 413  // Tools for emulating variadic templates in C++98.  The basic idea here is
 414  // stolen from the boost preprocessor metaprogramming library and cut down to
 415  // be just general enough for what we need.
 416  
 417  #define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_ ## n
 418  #define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_ ## n
 419  #define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_ ## n
 420  #define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_ ## n
 421  
 422  // To keep it as transparent as possible, the macros below have been generated
 423  // using python via the excellent cog.py code generation script.  This avoids
 424  // the need for a bunch of complex (but more general) preprocessor tricks as
 425  // used in boost.preprocessor.
 426  //
 427  // To rerun the code generation in place, use `cog.py -r tinyformat.h`
 428  // (see http://nedbatchelder.com/code/cog).  Alternatively you can just create
 429  // extra versions by hand.
 430  
 431  /*[[[cog
 432  maxParams = 16
 433  
 434  def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1):
 435      for j in range(startInd,maxParams+1):
 436          list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)])
 437          cog.outl(lineTemplate % {'j':j, 'list':list})
 438  
 439  makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s',
 440                    'class T%(i)d')
 441  
 442  cog.outl()
 443  makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s',
 444                    'const T%(i)d& v%(i)d')
 445  
 446  cog.outl()
 447  makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d')
 448  
 449  cog.outl()
 450  cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1')
 451  makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s',
 452                    'v%(i)d', startInd = 2)
 453  
 454  cog.outl()
 455  cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n    ' +
 456           ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)]))
 457  ]]]*/
 458  #define TINYFORMAT_ARGTYPES_1 class T1
 459  #define TINYFORMAT_ARGTYPES_2 class T1, class T2
 460  #define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3
 461  #define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4
 462  #define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5
 463  #define TINYFORMAT_ARGTYPES_6 class T1, class T2, class T3, class T4, class T5, class T6
 464  #define TINYFORMAT_ARGTYPES_7 class T1, class T2, class T3, class T4, class T5, class T6, class T7
 465  #define TINYFORMAT_ARGTYPES_8 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8
 466  #define TINYFORMAT_ARGTYPES_9 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9
 467  #define TINYFORMAT_ARGTYPES_10 class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8, class T9, class T10
 468  #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
 469  #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
 470  #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
 471  #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
 472  #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
 473  #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
 474  
 475  #define TINYFORMAT_VARARGS_1 const T1& v1
 476  #define TINYFORMAT_VARARGS_2 const T1& v1, const T2& v2
 477  #define TINYFORMAT_VARARGS_3 const T1& v1, const T2& v2, const T3& v3
 478  #define TINYFORMAT_VARARGS_4 const T1& v1, const T2& v2, const T3& v3, const T4& v4
 479  #define TINYFORMAT_VARARGS_5 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5
 480  #define TINYFORMAT_VARARGS_6 const T1& v1, const T2& v2, const T3& v3, const T4& v4, const T5& v5, const T6& v6
 481  #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
 482  #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
 483  #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
 484  #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
 485  #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
 486  #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
 487  #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
 488  #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
 489  #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
 490  #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
 491  
 492  #define TINYFORMAT_PASSARGS_1 v1
 493  #define TINYFORMAT_PASSARGS_2 v1, v2
 494  #define TINYFORMAT_PASSARGS_3 v1, v2, v3
 495  #define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4
 496  #define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5
 497  #define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6
 498  #define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7
 499  #define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8
 500  #define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9
 501  #define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10
 502  #define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
 503  #define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
 504  #define TINYFORMAT_PASSARGS_13 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
 505  #define TINYFORMAT_PASSARGS_14 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
 506  #define TINYFORMAT_PASSARGS_15 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
 507  #define TINYFORMAT_PASSARGS_16 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16
 508  
 509  #define TINYFORMAT_PASSARGS_TAIL_1
 510  #define TINYFORMAT_PASSARGS_TAIL_2 , v2
 511  #define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3
 512  #define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4
 513  #define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5
 514  #define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6
 515  #define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7
 516  #define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8
 517  #define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9
 518  #define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10
 519  #define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
 520  #define TINYFORMAT_PASSARGS_TAIL_12 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
 521  #define TINYFORMAT_PASSARGS_TAIL_13 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
 522  #define TINYFORMAT_PASSARGS_TAIL_14 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
 523  #define TINYFORMAT_PASSARGS_TAIL_15 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
 524  #define TINYFORMAT_PASSARGS_TAIL_16 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16
 525  
 526  #define TINYFORMAT_FOREACH_ARGNUM(m) \
 527      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)
 528  //[[[end]]]
 529  
 530  
 531  
 532  namespace detail {
 533  
 534  // Type-opaque holder for an argument to format(), with associated actions on
 535  // the type held as explicit function pointers.  This allows FormatArg's for
 536  // each argument to be allocated as a homogeneous array inside FormatList
 537  // whereas a naive implementation based on inheritance does not.
 538  class FormatArg
 539  {
 540      public:
 541          FormatArg() = default;
 542  
 543          template<typename T>
 544          explicit FormatArg(const T& value)
 545              : m_value(static_cast<const void*>(&value)),
 546              m_formatImpl(&formatImpl<T>),
 547              m_toIntImpl(&toIntImpl<T>)
 548          { }
 549  
 550          void format(std::ostream& out, const char* fmtBegin,
 551                      const char* fmtEnd, int ntrunc) const
 552          {
 553              TINYFORMAT_ASSERT(m_value);
 554              TINYFORMAT_ASSERT(m_formatImpl);
 555              m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
 556          }
 557  
 558          int toInt() const
 559          {
 560              TINYFORMAT_ASSERT(m_value);
 561              TINYFORMAT_ASSERT(m_toIntImpl);
 562              return m_toIntImpl(m_value);
 563          }
 564  
 565      private:
 566          template<typename T>
 567          TINYFORMAT_HIDDEN static void formatImpl(std::ostream& out, const char* fmtBegin,
 568                          const char* fmtEnd, int ntrunc, const void* value)
 569          {
 570              formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T*>(value));
 571          }
 572  
 573          template<typename T>
 574          TINYFORMAT_HIDDEN static int toIntImpl(const void* value)
 575          {
 576              return convertToInt<T>::invoke(*static_cast<const T*>(value));
 577          }
 578  
 579          const void* m_value{nullptr};
 580          void (*m_formatImpl)(std::ostream& out, const char* fmtBegin,
 581                               const char* fmtEnd, int ntrunc, const void* value){nullptr};
 582          int (*m_toIntImpl)(const void* value){nullptr};
 583  };
 584  
 585  
 586  // Parse and return an integer from the string c, as atoi()
 587  // On return, c is set to one past the end of the integer.
 588  inline int parseIntAndAdvance(const char*& c)
 589  {
 590      int i = 0;
 591      for (;*c >= '0' && *c <= '9'; ++c)
 592          i = 10*i + (*c - '0');
 593      return i;
 594  }
 595  
 596  // Parse width or precision `n` from format string pointer `c`, and advance it
 597  // to the next character. If an indirection is requested with `*`, the argument
 598  // is read from `args[argIndex]` and `argIndex` is incremented (or read
 599  // from `args[n]` in positional mode). Returns true if one or more
 600  // characters were read.
 601  inline bool parseWidthOrPrecision(int& n, const char*& c, bool positionalMode,
 602                                    const detail::FormatArg* args,
 603                                    int& argIndex, int numArgs)
 604  {
 605      if (*c >= '0' && *c <= '9') {
 606          n = parseIntAndAdvance(c);
 607      }
 608      else if (*c == '*') {
 609          ++c;
 610          n = 0;
 611          if (positionalMode) {
 612              int pos = parseIntAndAdvance(c) - 1;
 613              if (*c != '$')
 614                  TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
 615              if (pos >= 0 && pos < numArgs)
 616                  n = args[pos].toInt();
 617              else
 618                  TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
 619              ++c;
 620          }
 621          else {
 622              if (argIndex < numArgs)
 623                  n = args[argIndex++].toInt();
 624              else
 625                  TINYFORMAT_ERROR("tinyformat: Not enough arguments to read variable width or precision");
 626          }
 627      }
 628      else {
 629          return false;
 630      }
 631      return true;
 632  }
 633  
 634  // Print literal part of format string and return next format spec position.
 635  //
 636  // Skips over any occurrences of '%%', printing a literal '%' to the output.
 637  // The position of the first % character of the next nontrivial format spec is
 638  // returned, or the end of string.
 639  inline const char* printFormatStringLiteral(std::ostream& out, const char* fmt)
 640  {
 641      const char* c = fmt;
 642      for (;; ++c) {
 643          if (*c == '\0') {
 644              out.write(fmt, c - fmt);
 645              return c;
 646          }
 647          else if (*c == '%') {
 648              out.write(fmt, c - fmt);
 649              if (*(c+1) != '%')
 650                  return c;
 651              // for "%%", tack trailing % onto next literal section.
 652              fmt = ++c;
 653          }
 654      }
 655  }
 656  
 657  
 658  // Parse a format string and set the stream state accordingly.
 659  //
 660  // The format mini-language recognized here is meant to be the one from C99,
 661  // with the form "%[flags][width][.precision][length]type" with POSIX
 662  // positional arguments extension.
 663  //
 664  // POSIX positional arguments extension:
 665  // Conversions can be applied to the nth argument after the format in
 666  // the argument list, rather than to the next unused argument. In this case,
 667  // the conversion specifier character % (see below) is replaced by the sequence
 668  // "%n$", where n is a decimal integer in the range [1,{NL_ARGMAX}],
 669  // giving the position of the argument in the argument list. This feature
 670  // provides for the definition of format strings that select arguments
 671  // in an order appropriate to specific languages.
 672  //
 673  // The format can contain either numbered argument conversion specifications
 674  // (that is, "%n$" and "*m$"), or unnumbered argument conversion specifications
 675  // (that is, % and * ), but not both. The only exception to this is that %%
 676  // can be mixed with the "%n$" form. The results of mixing numbered and
 677  // unnumbered argument specifications in a format string are undefined.
 678  // When numbered argument specifications are used, specifying the Nth argument
 679  // requires that all the leading arguments, from the first to the (N-1)th,
 680  // are specified in the format string.
 681  //
 682  // In format strings containing the "%n$" form of conversion specification,
 683  // numbered arguments in the argument list can be referenced from the format
 684  // string as many times as required.
 685  //
 686  // Formatting options which can't be natively represented using the ostream
 687  // state are returned in spacePadPositive (for space padded positive numbers)
 688  // and ntrunc (for truncating conversions).  argIndex is incremented if
 689  // necessary to pull out variable width and precision.  The function returns a
 690  // pointer to the character after the end of the current format spec.
 691  inline const char* streamStateFromFormat(std::ostream& out, bool& positionalMode,
 692                                           bool& spacePadPositive,
 693                                           int& ntrunc, const char* fmtStart,
 694                                           const detail::FormatArg* args,
 695                                           int& argIndex, int numArgs)
 696  {
 697      TINYFORMAT_ASSERT(*fmtStart == '%');
 698      // Reset stream state to defaults.
 699      out.width(0);
 700      out.precision(6);
 701      out.fill(' ');
 702      // Reset most flags; ignore irrelevant unitbuf & skipws.
 703      out.unsetf(std::ios::adjustfield | std::ios::basefield |
 704                 std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
 705                 std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
 706      bool precisionSet = false;
 707      bool widthSet = false;
 708      int widthExtra = 0;
 709      const char* c = fmtStart + 1;
 710  
 711      // 1) Parse an argument index (if followed by '$') or a width possibly
 712      // preceded with '0' flag.
 713      if (*c >= '0' && *c <= '9') {
 714          const char tmpc = *c;
 715          int value = parseIntAndAdvance(c);
 716          if (*c == '$') {
 717              // value is an argument index
 718              if (value > 0 && value <= numArgs)
 719                  argIndex = value - 1;
 720              else
 721                  TINYFORMAT_ERROR("tinyformat: Positional argument out of range");
 722              ++c;
 723              positionalMode = true;
 724          }
 725          else if (positionalMode) {
 726              TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
 727          }
 728          else {
 729              if (tmpc == '0') {
 730                  // Use internal padding so that numeric values are
 731                  // formatted correctly, eg -00010 rather than 000-10
 732                  out.fill('0');
 733                  out.setf(std::ios::internal, std::ios::adjustfield);
 734              }
 735              if (value != 0) {
 736                  // Nonzero value means that we parsed width.
 737                  widthSet = true;
 738                  out.width(value);
 739              }
 740          }
 741      }
 742      else if (positionalMode) {
 743          TINYFORMAT_ERROR("tinyformat: Non-positional argument used after a positional one");
 744      }
 745      // 2) Parse flags and width if we did not do it in previous step.
 746      if (!widthSet) {
 747          // Parse flags
 748          for (;; ++c) {
 749              switch (*c) {
 750                  case '#':
 751                      out.setf(std::ios::showpoint | std::ios::showbase);
 752                      continue;
 753                  case '0':
 754                      // overridden by left alignment ('-' flag)
 755                      if (!(out.flags() & std::ios::left)) {
 756                          // Use internal padding so that numeric values are
 757                          // formatted correctly, eg -00010 rather than 000-10
 758                          out.fill('0');
 759                          out.setf(std::ios::internal, std::ios::adjustfield);
 760                      }
 761                      continue;
 762                  case '-':
 763                      out.fill(' ');
 764                      out.setf(std::ios::left, std::ios::adjustfield);
 765                      continue;
 766                  case ' ':
 767                      // overridden by show positive sign, '+' flag.
 768                      if (!(out.flags() & std::ios::showpos))
 769                          spacePadPositive = true;
 770                      continue;
 771                  case '+':
 772                      out.setf(std::ios::showpos);
 773                      spacePadPositive = false;
 774                      widthExtra = 1;
 775                      continue;
 776                  default:
 777                      break;
 778              }
 779              break;
 780          }
 781          // Parse width
 782          int width = 0;
 783          widthSet = parseWidthOrPrecision(width, c, positionalMode,
 784                                           args, argIndex, numArgs);
 785          if (widthSet) {
 786              if (width < 0) {
 787                  // negative widths correspond to '-' flag set
 788                  out.fill(' ');
 789                  out.setf(std::ios::left, std::ios::adjustfield);
 790                  width = -width;
 791              }
 792              out.width(width);
 793          }
 794      }
 795      // 3) Parse precision
 796      if (*c == '.') {
 797          ++c;
 798          int precision = 0;
 799          parseWidthOrPrecision(precision, c, positionalMode,
 800                                args, argIndex, numArgs);
 801          // Presence of `.` indicates precision set, unless the inferred value
 802          // was negative in which case the default is used.
 803          precisionSet = precision >= 0;
 804          if (precisionSet)
 805              out.precision(precision);
 806      }
 807      // 4) Ignore any C99 length modifier
 808      while (*c == 'l' || *c == 'h' || *c == 'L' ||
 809             *c == 'j' || *c == 'z' || *c == 't') {
 810          ++c;
 811      }
 812      // 5) We're up to the conversion specifier character.
 813      // Set stream flags based on conversion specifier (thanks to the
 814      // boost::format class for forging the way here).
 815      bool intConversion = false;
 816      switch (*c) {
 817          case 'u': case 'd': case 'i':
 818              out.setf(std::ios::dec, std::ios::basefield);
 819              intConversion = true;
 820              break;
 821          case 'o':
 822              out.setf(std::ios::oct, std::ios::basefield);
 823              intConversion = true;
 824              break;
 825          case 'X':
 826              out.setf(std::ios::uppercase);
 827              [[fallthrough]];
 828          case 'x': case 'p':
 829              out.setf(std::ios::hex, std::ios::basefield);
 830              intConversion = true;
 831              break;
 832          case 'E':
 833              out.setf(std::ios::uppercase);
 834              [[fallthrough]];
 835          case 'e':
 836              out.setf(std::ios::scientific, std::ios::floatfield);
 837              out.setf(std::ios::dec, std::ios::basefield);
 838              break;
 839          case 'F':
 840              out.setf(std::ios::uppercase);
 841              [[fallthrough]];
 842          case 'f':
 843              out.setf(std::ios::fixed, std::ios::floatfield);
 844              break;
 845          case 'A':
 846              out.setf(std::ios::uppercase);
 847              [[fallthrough]];
 848          case 'a':
 849  #           ifdef _MSC_VER
 850              // Workaround https://developercommunity.visualstudio.com/content/problem/520472/hexfloat-stream-output-does-not-ignore-precision-a.html
 851              // by always setting maximum precision on MSVC to avoid precision
 852              // loss for doubles.
 853              out.precision(13);
 854  #           endif
 855              out.setf(std::ios::fixed | std::ios::scientific, std::ios::floatfield);
 856              break;
 857          case 'G':
 858              out.setf(std::ios::uppercase);
 859              [[fallthrough]];
 860          case 'g':
 861              out.setf(std::ios::dec, std::ios::basefield);
 862              // As in boost::format, let stream decide float format.
 863              out.flags(out.flags() & ~std::ios::floatfield);
 864              break;
 865          case 'c':
 866              // Handled as special case inside formatValue()
 867              break;
 868          case 's':
 869              if (precisionSet)
 870                  ntrunc = static_cast<int>(out.precision());
 871              // Make %s print Booleans as "true" and "false"
 872              out.setf(std::ios::boolalpha);
 873              break;
 874          case 'n':
 875              // Not supported - will cause problems!
 876              TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported");
 877              break;
 878          case '\0':
 879              TINYFORMAT_ERROR("tinyformat: Conversion spec incorrectly "
 880                               "terminated by end of string");
 881              return c;
 882          default:
 883              break;
 884      }
 885      if (intConversion && precisionSet && !widthSet) {
 886          // "precision" for integers gives the minimum number of digits (to be
 887          // padded with zeros on the left).  This isn't really supported by the
 888          // iostreams, but we can approximately simulate it with the width if
 889          // the width isn't otherwise used.
 890          out.width(out.precision() + widthExtra);
 891          out.setf(std::ios::internal, std::ios::adjustfield);
 892          out.fill('0');
 893      }
 894      return c+1;
 895  }
 896  
 897  
 898  //------------------------------------------------------------------------------
 899  inline void formatImpl(std::ostream& out, const char* fmt,
 900                         const detail::FormatArg* args,
 901                         int numArgs)
 902  {
 903      // Saved stream state
 904      std::streamsize origWidth = out.width();
 905      std::streamsize origPrecision = out.precision();
 906      std::ios::fmtflags origFlags = out.flags();
 907      char origFill = out.fill();
 908  
 909      // "Positional mode" means all format specs should be of the form "%n$..."
 910      // with `n` an integer. We detect this in `streamStateFromFormat`.
 911      bool positionalMode = false;
 912      int argIndex = 0;
 913      while (true) {
 914          fmt = printFormatStringLiteral(out, fmt);
 915          if (*fmt == '\0') {
 916              if (!positionalMode && argIndex < numArgs) {
 917                  TINYFORMAT_ERROR("tinyformat: Not enough conversion specifiers in format string");
 918              }
 919              break;
 920          }
 921          bool spacePadPositive = false;
 922          int ntrunc = -1;
 923          const char* fmtEnd = streamStateFromFormat(out, positionalMode, spacePadPositive, ntrunc, fmt,
 924                                                     args, argIndex, numArgs);
 925          // NB: argIndex may be incremented by reading variable width/precision
 926          // in `streamStateFromFormat`, so do the bounds check here.
 927          if (argIndex >= numArgs) {
 928              TINYFORMAT_ERROR("tinyformat: Too many conversion specifiers in format string");
 929              return;
 930          }
 931          const FormatArg& arg = args[argIndex];
 932          // Format the arg into the stream.
 933          if (!spacePadPositive) {
 934              arg.format(out, fmt, fmtEnd, ntrunc);
 935          }
 936          else {
 937              // The following is a special case with no direct correspondence
 938              // between stream formatting and the printf() behaviour.  Simulate
 939              // it crudely by formatting into a temporary string stream and
 940              // munging the resulting string.
 941              std::ostringstream tmpStream;
 942              tmpStream.copyfmt(out);
 943              tmpStream.setf(std::ios::showpos);
 944              arg.format(tmpStream, fmt, fmtEnd, ntrunc);
 945              std::string result = tmpStream.str(); // allocates... yuck.
 946              for (size_t i = 0, iend = result.size(); i < iend; ++i) {
 947                  if (result[i] == '+')
 948                      result[i] = ' ';
 949              }
 950              out << result;
 951          }
 952          if (!positionalMode)
 953              ++argIndex;
 954          fmt = fmtEnd;
 955      }
 956  
 957      // Restore stream state
 958      out.width(origWidth);
 959      out.precision(origPrecision);
 960      out.flags(origFlags);
 961      out.fill(origFill);
 962  }
 963  
 964  } // namespace detail
 965  
 966  
 967  /// List of template arguments format(), held in a type-opaque way.
 968  ///
 969  /// A const reference to FormatList (typedef'd as FormatListRef) may be
 970  /// conveniently used to pass arguments to non-template functions: All type
 971  /// information has been stripped from the arguments, leaving just enough of a
 972  /// common interface to perform formatting as required.
 973  class FormatList
 974  {
 975      public:
 976          FormatList(detail::FormatArg* args, int N)
 977              : m_args(args), m_N(N) { }
 978  
 979          friend void vformat(std::ostream& out, const char* fmt,
 980                              const FormatList& list);
 981  
 982      private:
 983          const detail::FormatArg* m_args;
 984          int m_N;
 985  };
 986  
 987  /// Reference to type-opaque format list for passing to vformat()
 988  typedef const FormatList& FormatListRef;
 989  
 990  
 991  namespace detail {
 992  
 993  // Format list subclass with fixed storage to avoid dynamic allocation
 994  template<int N>
 995  class FormatListN : public FormatList
 996  {
 997      public:
 998  #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
 999          template<typename... Args>
1000          explicit FormatListN(const Args&... args)
1001              : FormatList(&m_formatterStore[0], N),
1002              m_formatterStore { FormatArg(args)... }
1003          { static_assert(sizeof...(args) == N, "Number of args must be N"); }
1004  #else // C++98 version
1005          void init(int) {}
1006  #       define TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR(n)                \
1007                                                                          \
1008          template<TINYFORMAT_ARGTYPES(n)>                                \
1009          FormatListN(TINYFORMAT_VARARGS(n))                              \
1010              : FormatList(&m_formatterStore[0], n)                       \
1011          { TINYFORMAT_ASSERT(n == N); init(0, TINYFORMAT_PASSARGS(n)); } \
1012                                                                          \
1013          template<TINYFORMAT_ARGTYPES(n)>                                \
1014          void init(int i, TINYFORMAT_VARARGS(n))                         \
1015          {                                                               \
1016              m_formatterStore[i] = FormatArg(v1);                        \
1017              init(i+1 TINYFORMAT_PASSARGS_TAIL(n));                      \
1018          }
1019  
1020          TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR)
1021  #       undef TINYFORMAT_MAKE_FORMATLIST_CONSTRUCTOR
1022  #endif
1023          FormatListN(const FormatListN& other)
1024              : FormatList(&m_formatterStore[0], N)
1025          { std::copy(&other.m_formatterStore[0], &other.m_formatterStore[N],
1026                      &m_formatterStore[0]); }
1027  
1028      private:
1029          FormatArg m_formatterStore[N];
1030  };
1031  
1032  // Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
1033  template<> class FormatListN<0> : public FormatList
1034  {
1035  public:
1036      FormatListN() : FormatList(nullptr, 0) {}
1037  };
1038  
1039  } // namespace detail
1040  
1041  
1042  //------------------------------------------------------------------------------
1043  // Primary API functions
1044  
1045  #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
1046  
1047  /// Make type-agnostic format list from list of template arguments.
1048  ///
1049  /// The exact return type of this function is an implementation detail and
1050  /// shouldn't be relied upon.  Instead it should be stored as a FormatListRef:
1051  ///
1052  ///   FormatListRef formatList = makeFormatList( /*...*/ );
1053  template<typename... Args>
1054  detail::FormatListN<sizeof...(Args)> makeFormatList(const Args&... args)
1055  {
1056      return detail::FormatListN<sizeof...(args)>(args...);
1057  }
1058  
1059  #else // C++98 version
1060  
1061  inline detail::FormatListN<0> makeFormatList()
1062  {
1063      return detail::FormatListN<0>();
1064  }
1065  #define TINYFORMAT_MAKE_MAKEFORMATLIST(n)                     \
1066  template<TINYFORMAT_ARGTYPES(n)>                              \
1067  detail::FormatListN<n> makeFormatList(TINYFORMAT_VARARGS(n))  \
1068  {                                                             \
1069      return detail::FormatListN<n>(TINYFORMAT_PASSARGS(n));    \
1070  }
1071  TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
1072  #undef TINYFORMAT_MAKE_MAKEFORMATLIST
1073  
1074  #endif
1075  
1076  /// Format list of arguments to the stream according to the given format string.
1077  ///
1078  /// The name vformat() is chosen for the semantic similarity to vprintf(): the
1079  /// list of format arguments is held in a single function argument.
1080  inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
1081  {
1082      detail::formatImpl(out, fmt, list.m_args, list.m_N);
1083  }
1084  
1085  
1086  #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
1087  
1088  /// Format list of arguments to the stream according to given format string.
1089  template<typename... Args>
1090  void format(std::ostream& out, FormatStringCheck<sizeof...(Args)> fmt, const Args&... args)
1091  {
1092      vformat(out, fmt, makeFormatList(args...));
1093  }
1094  
1095  /// Format list of arguments according to the given format string and return
1096  /// the result as a string.
1097  template<typename... Args>
1098  std::string format(FormatStringCheck<sizeof...(Args)> fmt, const Args&... args)
1099  {
1100      std::ostringstream oss;
1101      format(oss, fmt, args...);
1102      return oss.str();
1103  }
1104  
1105  /// Format list of arguments to std::cout, according to the given format string
1106  template<typename... Args>
1107  void printf(FormatStringCheck<sizeof...(Args)> fmt, const Args&... args)
1108  {
1109      format(std::cout, fmt, args...);
1110  }
1111  
1112  template<typename... Args>
1113  void printfln(FormatStringCheck<sizeof...(Args)> fmt, const Args&... args)
1114  {
1115      format(std::cout, fmt, args...);
1116      std::cout << '\n';
1117  }
1118  
1119  
1120  #else // C++98 version
1121  
1122  inline void format(std::ostream& out, const char* fmt)
1123  {
1124      vformat(out, fmt, makeFormatList());
1125  }
1126  
1127  inline std::string format(const char* fmt)
1128  {
1129      std::ostringstream oss;
1130      format(oss, fmt);
1131      return oss.str();
1132  }
1133  
1134  inline void printf(const char* fmt)
1135  {
1136      format(std::cout, fmt);
1137  }
1138  
1139  inline void printfln(const char* fmt)
1140  {
1141      format(std::cout, fmt);
1142      std::cout << '\n';
1143  }
1144  
1145  #define TINYFORMAT_MAKE_FORMAT_FUNCS(n)                                   \
1146                                                                            \
1147  template<TINYFORMAT_ARGTYPES(n)>                                          \
1148  void format(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n))    \
1149  {                                                                         \
1150      vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n)));            \
1151  }                                                                         \
1152                                                                            \
1153  template<TINYFORMAT_ARGTYPES(n)>                                          \
1154  std::string format(const char* fmt, TINYFORMAT_VARARGS(n))                \
1155  {                                                                         \
1156      std::ostringstream oss;                                               \
1157      format(oss, fmt, TINYFORMAT_PASSARGS(n));                             \
1158      return oss.str();                                                     \
1159  }                                                                         \
1160                                                                            \
1161  template<TINYFORMAT_ARGTYPES(n)>                                          \
1162  void printf(const char* fmt, TINYFORMAT_VARARGS(n))                       \
1163  {                                                                         \
1164      format(std::cout, fmt, TINYFORMAT_PASSARGS(n));                       \
1165  }                                                                         \
1166                                                                            \
1167  template<TINYFORMAT_ARGTYPES(n)>                                          \
1168  void printfln(const char* fmt, TINYFORMAT_VARARGS(n))                     \
1169  {                                                                         \
1170      format(std::cout, fmt, TINYFORMAT_PASSARGS(n));                       \
1171      std::cout << '\n';                                                    \
1172  }
1173  
1174  TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)
1175  #undef TINYFORMAT_MAKE_FORMAT_FUNCS
1176  
1177  #endif
1178  
1179  } // namespace tinyformat
1180  
1181  // Added for Limenka:
1182  /** Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for details) */
1183  #define strprintf tfm::format
1184  
1185  #endif // TINYFORMAT_H_INCLUDED
1186