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