add support for printing unicode and wide characters

master
Daniel Kolesa 2017-12-31 15:32:33 +01:00
parent b2ee5c1bd0
commit fb2f9e3b0e
1 changed files with 58 additions and 9 deletions

View File

@ -223,6 +223,13 @@ namespace detail {
template<typename T>
inline constexpr bool is_tuple_like = decltype(tuple_like_test<T>(0))::value;
/* character type tests */
template<typename C>
inline constexpr bool is_character = std::is_same_v<C, char> ||
std::is_same_v<C, wchar_t> ||
std::is_same_v<C, char16_t> ||
std::is_same_v<C, char32_t>;
/* test if format traits are available for the type */
template<typename T, typename R>
static std::true_type test_tofmt(decltype(format_traits<T>::to_format(
@ -236,10 +243,10 @@ namespace detail {
template<typename T, typename R>
inline constexpr bool fmt_tofmt_test = decltype(test_tofmt<T, R>(0))::value;
inline int wc_to_mb_loc(wchar_t c, char *buf, std::locale const &loc) {
auto &f = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
template<typename C, typename F>
inline int ac_to_mb(C c, F const &f, char *buf) {
std::mbstate_t mb{};
wchar_t const *fromn;
C const *fromn;
char *ton;
auto ret = f.out(mb, &c, &c + 1, fromn, buf, &buf[MB_LEN_MAX], ton);
if (ret != std::codecvt_base::ok) {
@ -247,6 +254,12 @@ namespace detail {
}
return ton - &buf[0];
}
inline int wc_to_mb_loc(wchar_t c, char *buf, std::locale const &loc) {
auto &f = std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t>>(loc);
std::mbstate_t mb{};
return ac_to_mb(c, f, buf);
}
}
/** @brief A structure implementing type safe C-style formatting.
@ -998,10 +1011,38 @@ private:
write_spaces(writer, n, false);
}
template<typename R, typename C>
void write_char_raw(R &writer, C val) const {
if constexpr(std::is_same_v<C, char>) {
writer.put(val);
} else {
/* TODO: replace this ugly thing with custom unicode APIs */
char buf[MB_LEN_MAX];
int n;
if constexpr(std::is_same_v<C, wchar_t>) {
/* convert according to locale */
n = detail::wc_to_mb_loc(val, buf, p_loc);
} else {
fmt_codecvt<C> fac{};
n = detail::ac_to_mb(val, fac, buf);
}
if (n < 0) {
/* replacement character */
writer.put(0xEF);
writer.put(0xBF);
writer.put(0xBD);
} else {
for (int i = 0; i < n; ++i) {
writer.put(buf[i]);
}
}
}
}
/* char values */
template<typename R>
void write_char(R &writer, bool escape, char val) const {
if (escape) {
template<typename R, typename C>
void write_char(R &writer, bool escape, C val) const {
if (escape && (val <= 0x7F)) {
char const *esc = detail::escape_fmt_char(val, '\'');
if (esc) {
char buf[6];
@ -1018,10 +1059,10 @@ private:
write_spaces(writer, 1 + escape * 2, true);
if (escape) {
writer.put('\'');
writer.put(val);
write_char_raw(writer, val);
writer.put('\'');
} else {
writer.put(val);
write_char_raw(writer, val);
}
write_spaces(writer, 1 + escape * 2, false);
}
@ -1258,7 +1299,7 @@ private:
return;
}
/* character values */
if constexpr(std::is_same_v<T, char>) {
if constexpr(detail::is_character<T>) {
if (spec() != 's' && spec() != 'c') {
throw format_error{"cannot format chars with the given spec"};
}
@ -1513,6 +1554,14 @@ private:
~fmt_num_put() {}
};
template<typename C>
struct fmt_codecvt final: std::codecvt<C, char, std::mbstate_t> {
fmt_codecvt(std::size_t refs = 0):
std::codecvt<C, char, std::mbstate_t>(refs)
{}
~fmt_codecvt() {}
};
string_range p_fmt;
std::locale p_loc;
char p_buf[32];