From b98ffb7dbdb7d97e14ea65fad757cb5bb853d98b Mon Sep 17 00:00:00 2001 From: Victor Zverovich Date: Sun, 4 Sep 2022 20:20:59 -0700 Subject: [PATCH] Improve locale handling --- include/fmt/format-inl.h | 25 +----------- include/fmt/format.h | 84 ++++++++++++++++++++++++---------------- include/fmt/xchar.h | 21 ++++++++++ test/xchar-test.cc | 10 ++--- 4 files changed, 77 insertions(+), 63 deletions(-) diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index 465dd6b0..8721888d 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -129,29 +129,6 @@ FMT_FUNC auto write_loc(appender out, basic_format_arg value, #endif return false; } - -struct localizer { - appender out; - const format_specs& specs; - std::string sep; - std::string grouping; - std::string decimal_point; - - template ::value)> - auto operator()(T value) -> bool { - auto arg = make_write_int_arg(value, specs.sign); - write_int(out, static_cast>(arg.abs_value), arg.prefix, - specs, digit_grouping(grouping, sep)); - return true; - } - - template ::value)> - auto operator()(T) -> bool { - return false; - } - - auto operator()(...) -> bool { return false; } -}; } // namespace detail template typename Locale::id format_facet::id; @@ -168,7 +145,7 @@ FMT_API FMT_FUNC auto format_facet::do_put( appender out, basic_format_arg val, const format_specs& specs) const -> bool { return visit_format_arg( - detail::localizer{out, specs, separator_, grouping_, decimal_point_}, + detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_}, val); } #endif diff --git a/include/fmt/format.h b/include/fmt/format.h index c2bff6c4..43dc215a 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -739,6 +739,21 @@ inline auto code_point_index(basic_string_view s, size_t n) string_view(reinterpret_cast(s.data()), s.size()), n); } +template struct is_integral : std::is_integral {}; +template <> struct is_integral : std::true_type {}; +template <> struct is_integral : std::true_type {}; + +template +using is_signed = + std::integral_constant::is_signed || + std::is_same::value>; + +template +using is_integer = + bool_constant::value && !std::is_same::value && + !std::is_same::value && + !std::is_same::value>; + #ifndef FMT_USE_FLOAT128 # ifdef __SIZEOF_FLOAT128__ # define FMT_USE_FLOAT128 1 @@ -1017,15 +1032,6 @@ template class format_facet : public Locale::facet { FMT_BEGIN_DETAIL_NAMESPACE -template struct is_integral : std::is_integral {}; -template <> struct is_integral : std::true_type {}; -template <> struct is_integral : std::true_type {}; - -template -using is_signed = - std::integral_constant::is_signed || - std::is_same::value>; - // Returns true if value is negative, false otherwise. // Same as `value < 0` but doesn't produce warnings if T is an unsigned type. template ::value)> @@ -1989,7 +1995,7 @@ template class digit_grouping { grouping_ = sep.grouping; if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep); } - digit_grouping(std::string grouping, std::string sep) + digit_grouping(std::string grouping, std::basic_string sep) : grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {} bool has_separator() const { return !thousands_sep_.empty(); } @@ -2047,23 +2053,16 @@ auto write_int(OutputIt out, UInt value, unsigned prefix, }); } -// Writes value with localization. +// Writes localized value. FMT_API auto write_loc(appender out, basic_format_arg value, const format_specs& specs, locale_ref loc) -> bool; + template inline auto write_loc(OutputIt, basic_format_arg>, const basic_format_specs&, locale_ref) -> bool { return false; } -template -auto write_int(OutputIt& out, UInt value, unsigned prefix, - const basic_format_specs& specs, locale_ref loc) -> bool { - auto grouping = digit_grouping(loc); - out = write_int(out, value, prefix, specs, grouping); - return true; -} - FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) { prefix |= prefix != 0 ? value << 8 : value; prefix += (1u + (value > 0xff ? 1 : 0)) << 24; @@ -2090,20 +2089,39 @@ FMT_CONSTEXPR auto make_write_int_arg(T value, sign_t sign) return {abs_value, prefix}; } +template struct loc_writer { + buffer_appender out; + const basic_format_specs& specs; + std::basic_string sep; + std::string grouping; + std::basic_string decimal_point; + + template ::value)> + auto operator()(T value) -> bool { + auto arg = make_write_int_arg(value, specs.sign); + write_int(out, static_cast>(arg.abs_value), arg.prefix, + specs, digit_grouping(grouping, sep)); + return true; + } + + template ::value)> + auto operator()(T) -> bool { + return false; + } + + auto operator()(...) -> bool { return false; } +}; + template FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, const basic_format_specs& specs, - locale_ref loc) -> OutputIt { + locale_ref) -> OutputIt { static_assert(std::is_same>::value, ""); auto abs_value = arg.abs_value; auto prefix = arg.prefix; switch (specs.type) { case presentation_type::none: case presentation_type::dec: { - if (specs.localized && - write_int(out, static_cast>(abs_value), prefix, - specs, loc)) - return out; auto num_digits = count_digits(abs_value); return write_int( out, num_digits, prefix, specs, [=](reserve_iterator it) { @@ -2163,6 +2181,10 @@ template & specs, locale_ref loc) -> OutputIt { + if (specs.localized && + write_loc(out, make_arg>(value), specs, loc)) { + return out; + } return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs, loc); } @@ -2174,6 +2196,10 @@ template & specs, locale_ref loc) -> OutputIt { + if (specs.localized && + write_loc(out, make_arg>(value), specs, loc)) { + return out; + } return write_int(out, make_write_int_arg(value, specs.sign), specs, loc); } @@ -3455,12 +3481,6 @@ template struct custom_formatter { template void operator()(T) const {} }; -template -using is_integer = - bool_constant::value && !std::is_same::value && - !std::is_same::value && - !std::is_same::value>; - template class width_checker { public: explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {} @@ -4171,10 +4191,6 @@ void vformat_to(buffer& buf, basic_string_view fmt, begin = parse_format_specs(begin, end, handler); if (begin == end || *begin != '}') on_error("missing '}' in format string"); - if (specs.localized && - write_loc(context.out(), arg, specs, context.locale())) { - return begin; - } auto f = arg_formatter{context.out(), specs, context.locale()}; context.advance_to(visit_format_arg(f, arg)); return begin; diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index 3b5bc15c..5fb5c815 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -12,11 +12,32 @@ #include "format.h" +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR +# include +#endif + FMT_BEGIN_NAMESPACE namespace detail { + template using is_exotic_char = bool_constant::value>; + +template +auto write_loc(OutputIt out, basic_format_arg> val, + const basic_format_specs& specs, locale_ref loc) + -> bool { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR + auto& numpunct = + std::use_facet>(loc.get()); + auto separator = std::wstring(); + auto grouping = numpunct.grouping(); + if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep()); + return visit_format_arg( + loc_writer{out, specs, separator, grouping, {}}, val); +#endif + return false; } +} // namespace detail FMT_MODULE_EXPORT_BEGIN diff --git a/test/xchar-test.cc b/test/xchar-test.cc index 3eec7384..dd45826d 100644 --- a/test/xchar-test.cc +++ b/test/xchar-test.cc @@ -448,11 +448,11 @@ TEST(locale_test, int_formatter) { auto f = fmt::formatter(); auto parse_ctx = fmt::format_parse_context("L"); f.parse(parse_ctx); - char buf[10] = {}; - fmt::basic_format_context format_ctx( - buf, {}, fmt::detail::locale_ref(loc)); - *f.format(12345, format_ctx) = 0; - EXPECT_STREQ("12,345", buf); + auto buf = fmt::memory_buffer(); + fmt::basic_format_context format_ctx( + fmt::appender(buf), {}, fmt::detail::locale_ref(loc)); + f.format(12345, format_ctx); + EXPECT_EQ(fmt::to_string(buf), "12,345"); } FMT_BEGIN_NAMESPACE