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