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