Skip to content

Commit 2261901

Browse files
committed
Make tinyformat aware of locales.
Without all this, you will get situations where format("%g",123.45) might return "123,45" if it happens to be running on a machine with locale "fr_FR", for example. If that is written to an output file that is later read on a computer using a locale with '.' as the decimal mark, it could mis-parse as "123.0", leading to hilarious and tragic results. It seems like the safe thing is to force the string-returning varieties of tinyformat to use classic locale ('.' decimal), with an optional means for the odd users to purposely specify a different (or native) locale. It also seems that the best policy for the stream-appending varieties of format() is to use the local already associated with those streams (C++ lets each stream have its own locale). 1. format() to an existing stream (including cout) is now documented to honor the stream's existing locale (as it always did). 2. format() that returns a string now will always format the string using the classic "C" locale, in particular meaning that floating point numbers will use '.' as the decimal mark, regardless of the "native" locale set by the OS or process that may have an unexpected choice of decimal point. 3. Add a new variety of format() that returns a string, but which takes an explicit locale, for cases where you don't want the "C" locale (for example, when generating strings that will appear in a UI that you want localized properly). You can make this variety use the current locale by passing std::locale() as the first argument (the default constructor of std::locale just grabs the global process locale).
1 parent 689695c commit 2261901

File tree

1 file changed

+29
-1
lines changed

1 file changed

+29
-1
lines changed

tinyformat.h

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -943,6 +943,7 @@ TINYFORMAT_FOREACH_ARGNUM(TINYFORMAT_MAKE_MAKEFORMATLIST)
943943
#endif
944944

945945
/// Format list of arguments to the stream according to the given format string.
946+
/// This honors the stream's existing locale conventions.
946947
///
947948
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
948949
/// list of format arguments is held in a single function argument.
@@ -955,23 +956,40 @@ inline void vformat(std::ostream& out, const char* fmt, FormatListRef list)
955956
#ifdef TINYFORMAT_USE_VARIADIC_TEMPLATES
956957

957958
/// Format list of arguments to the stream according to given format string.
959+
/// This honors the stream's existing locale conventions.
958960
template<typename... Args>
959961
void format(std::ostream& out, const char* fmt, const Args&... args)
960962
{
961963
vformat(out, fmt, makeFormatList(args...));
962964
}
963965

964966
/// Format list of arguments according to the given format string and return
965-
/// the result as a string.
967+
/// the result as a string, using classic "C" locale conventions (e.g.,
968+
/// using '.' as decimal mark).
966969
template<typename... Args>
967970
std::string format(const char* fmt, const Args&... args)
968971
{
969972
std::ostringstream oss;
973+
oss.imbue (std::locale::classic()); // force "C" locale with '.' decimal
974+
format(oss, fmt, args...);
975+
return oss.str();
976+
}
977+
978+
/// Format list of arguments according to the given format string and return
979+
/// the result as a string, using an explicit locale. Passing loc as a
980+
/// default-constructed std::locale will result in adhering to the current
981+
/// "native" locale set by the OS.
982+
template<typename... Args>
983+
std::string format(const std::locale& loc, const char* fmt, const Args&... args)
984+
{
985+
std::ostringstream oss;
986+
oss.imbue (loc);
970987
format(oss, fmt, args...);
971988
return oss.str();
972989
}
973990

974991
/// Format list of arguments to std::cout, according to the given format string
992+
/// This honors std::out's existing locale conventions.
975993
template<typename... Args>
976994
void printf(const char* fmt, const Args&... args)
977995
{
@@ -1023,6 +1041,16 @@ template<TINYFORMAT_ARGTYPES(n)> \
10231041
std::string format(const char* fmt, TINYFORMAT_VARARGS(n)) \
10241042
{ \
10251043
std::ostringstream oss; \
1044+
oss.imbue (std::locale::classic()); \
1045+
format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
1046+
return oss.str(); \
1047+
} \
1048+
\
1049+
template<TINYFORMAT_ARGTYPES(n)> \
1050+
std::string format(const std::locale& loc, const char* fmt, TINYFORMAT_VARARGS(n)) \
1051+
{ \
1052+
std::ostringstream oss; \
1053+
oss.imbue (loc); \
10261054
format(oss, fmt, TINYFORMAT_PASSARGS(n)); \
10271055
return oss.str(); \
10281056
} \

0 commit comments

Comments
 (0)