diff --git a/tinyformat.h b/tinyformat.h index 85a22c1..0b59ef9 100644 --- a/tinyformat.h +++ b/tinyformat.h @@ -955,6 +955,7 @@ inline void vformat(std::ostream& out, const char* fmt, FormatListRef list) #ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES /// Format list of arguments to the stream according to given format string. +/// This honors the stream's existing locale conventions. template void format(std::ostream& out, const char* fmt, const Args&... args) { @@ -962,7 +963,7 @@ void format(std::ostream& out, const char* fmt, const Args&... args) } /// Format list of arguments according to the given format string and return -/// the result as a string. +/// the result as a string, honoring the current global native locale. template std::string format(const char* fmt, const Args&... args) { @@ -972,6 +973,7 @@ std::string format(const char* fmt, const Args&... args) } /// Format list of arguments to std::cout, according to the given format string +/// This honors std::out's existing locale conventions. template void printf(const char* fmt, const Args&... args) { @@ -986,6 +988,44 @@ void printfln(const char* fmt, const Args&... args) } +/// Formatting functions ending in _c force the classic "C" locale (i.e. '.' +/// for decimal). They may be more expensive than the default functions, +/// but they are useful when you MUST be locale-independent (like for +/// persistent saved output when you always need identical formatting +/// that should not vary, or be mis-parsed if written by a computer set +/// up for one locale but read by a computer with a different locale). +template +void format_c(std::ostream& out, const char* fmt, const Args&... args) +{ + // Force "C" locale but save the previous one asssociated with the stream + std::locale oldloc = out.imbue (std::locale::classic()); + vformat(out, fmt, makeFormatList(args...)); + out.imbue (oldloc); // restore the original locale +} + +template +std::string format_c(const char* fmt, const Args&... args) +{ + std::ostringstream oss; + oss.imbue (std::locale::classic()); // force "C" locale with '.' decimal + format(oss, fmt, args...); + return oss.str(); +} + +template +void printf_c(const char* fmt, const Args&... args) +{ + format_c(std::cout, fmt, args...); +} + +template +void printfln_c(const char* fmt, const Args&... args) +{ + format_c(std::cout, fmt, args...); + std::cout << '\n'; +} + + #else // C++98 version inline void format(std::ostream& out, const char* fmt) @@ -1011,6 +1051,31 @@ inline void printfln(const char* fmt) std::cout << '\n'; } +inline void format_c(std::ostream& out, const char* fmt) +{ + std::locale oldloc = out.imbue (std::locale::classic()); + vformat(out, fmt, makeFormatList()); + out.imbue (oldloc); // restore the original locale +} + +inline std::string format_c(const char* fmt) +{ + std::ostringstream oss; + format_c(oss, fmt); + return oss.str(); +} + +inline void printf_c(const char* fmt) +{ + format_c(std::cout, fmt); +} + +inline void printfln_c(const char* fmt) +{ + format_c(std::cout, fmt); + std::cout << '\n'; +} + #define TINYFORMAT_MAKE_FORMAT_FUNCS(n) \ \ template \ @@ -1038,6 +1103,36 @@ void printfln(const char* fmt, TINYFORMAT_VARARGS(n)) \ { \ format(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ std::cout << '\n'; \ +} \ + \ +template \ +void format_c(std::ostream& out, const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + std::locale oldloc = out.imbue (std::locale::classic()); \ + vformat(out, fmt, makeFormatList(TINYFORMAT_PASSARGS(n))); \ + out.imbue (oldloc); \ +} \ + \ +template \ +std::string format_c(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + std::ostringstream oss; \ + oss.imbue (std::locale::classic()); \ + format(oss, fmt, TINYFORMAT_PASSARGS(n)); \ + return oss.str(); \ +} \ + \ +template \ +void printf_c(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format_c(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ +} \ + \ +template \ +void printfln_c(const char* fmt, TINYFORMAT_VARARGS(n)) \ +{ \ + format_c(std::cout, fmt, TINYFORMAT_PASSARGS(n)); \ + std::cout << '\n'; \ } TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_FORMAT_FUNCS)