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