From db28b668924a5bf71ddd3713d54d6e08e68b138e Mon Sep 17 00:00:00 2001 From: q66 Date: Sun, 12 Nov 2017 20:09:30 +0100 Subject: [PATCH] workaround the awful bullshit when formatting numbers under locale Because of the C++ locale APIs and libstdc++ being worthless trash, we need to resort to this kind of nonsense in order to avoid gibberish when dealing with grouping and decimal separators. Libc++ gets this right (comes up with ASCII style representations when requesting locale facets dealing with char type) but for some dumb reason libstdc++ comes up with representations that are garbage even when using a UTF-8 locale, so I guess we'll deal with it this way for the time being... That said, all of this code is probably broken on systems that don't use Unicode and honestly I don't care. --- ostd/format.hh | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/ostd/format.hh b/ostd/format.hh index ebc1068..fbd32f0 100644 --- a/ostd/format.hh +++ b/ostd/format.hh @@ -1015,7 +1015,7 @@ private: template void write_int(R &writer, bool ptr, bool neg, T val) const { /* binary representation is the biggest, assume grouping */ - char buf[sizeof(T) * CHAR_BIT * 2]; + char buf[sizeof(T) * CHAR_BIT * (MB_CUR_MAX + 1)]; std::size_t n = 0; char isp = spec(); @@ -1035,15 +1035,19 @@ private: buf[n++] = '0'; } - auto const &fac = std::use_facet>(p_loc); + /* this is bullshit */ + auto const &fac = std::use_facet>(p_loc); auto const &grp = fac.grouping(); - char tsep = fac.thousands_sep(); + char tseps[MB_CUR_MAX]; + int ntsep = wctomb(tseps, fac.thousands_sep()); auto grpp = reinterpret_cast(grp.data()); unsigned char grpn = *grpp; for (; val; val /= base) { if (!ptr && *grpp) { if (!grpn) { - buf[n++] = tsep; + for (int i = ntsep - 1; i >= 0; --i) { + buf[n++] = tseps[i]; + } if (*(grpp + 1)) { ++grpp; } @@ -1145,9 +1149,11 @@ private: } st.flags(fl); + /* this is also bullshit */ fmt_num_put nump; nump.put( - fmt_out{&writer}, st, (p_flags & FMT_FLAG_ZERO) ? '0' : ' ', val + fmt_out{&writer}, st, + (p_flags & FMT_FLAG_ZERO) ? L'0' : L' ', val ); } @@ -1424,16 +1430,20 @@ private: } } + /* but most of all, this part is the biggest bullshit */ template struct fmt_out { using iterator_category = std::output_iterator_tag; - using value_type = char; - using pointer = char *; - using reference = char &; - using difference_type = typename std::char_traits::off_type; + using value_type = wchar_t; + using pointer = wchar_t *; + using reference = wchar_t &; + using difference_type = typename std::char_traits::off_type; - fmt_out &operator=(char c) { - p_out->put(c); + fmt_out &operator=(wchar_t c) { + char buf[MB_CUR_MAX]; + for (int i = 0, j = wctomb(buf, c); i < j; ++i) { + p_out->put(buf[i]); + } return *this; } @@ -1445,9 +1455,9 @@ private: }; template - struct fmt_num_put final: std::num_put> { + struct fmt_num_put final: std::num_put> { fmt_num_put(std::size_t refs = 0): - std::num_put>(refs) + std::num_put>(refs) {} ~fmt_num_put() {} };