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.
master
Daniel Kolesa 2017-11-12 20:09:30 +01:00
parent 84572dfd01
commit db28b66892
1 changed files with 23 additions and 13 deletions

View File

@ -1015,7 +1015,7 @@ private:
template<typename R, typename T>
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<std::numpunct<char>>(p_loc);
/* this is bullshit */
auto const &fac = std::use_facet<std::numpunct<wchar_t>>(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<unsigned char const *>(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<R> nump;
nump.put(
fmt_out<R>{&writer}, st, (p_flags & FMT_FLAG_ZERO) ? '0' : ' ', val
fmt_out<R>{&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<typename R>
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<char>::off_type;
using value_type = wchar_t;
using pointer = wchar_t *;
using reference = wchar_t &;
using difference_type = typename std::char_traits<wchar_t>::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<typename R>
struct fmt_num_put final: std::num_put<char, fmt_out<R>> {
struct fmt_num_put final: std::num_put<wchar_t, fmt_out<R>> {
fmt_num_put(std::size_t refs = 0):
std::num_put<char, fmt_out<R>>(refs)
std::num_put<wchar_t, fmt_out<R>>(refs)
{}
~fmt_num_put() {}
};