Improve locale handling

This commit is contained in:
Victor Zverovich 2022-09-04 20:20:59 -07:00
parent bac53951b8
commit b98ffb7dbd
4 changed files with 77 additions and 63 deletions

View File

@ -129,29 +129,6 @@ FMT_FUNC auto write_loc(appender out, basic_format_arg<format_context> value,
#endif
return false;
}
struct localizer {
appender out;
const format_specs& specs;
std::string sep;
std::string grouping;
std::string decimal_point;
template <typename T, FMT_ENABLE_IF(detail::is_integer<T>::value)>
auto operator()(T value) -> bool {
auto arg = make_write_int_arg(value, specs.sign);
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
specs, digit_grouping<char>(grouping, sep));
return true;
}
template <typename T, FMT_ENABLE_IF(detail::is_floating_point<T>::value)>
auto operator()(T) -> bool {
return false;
}
auto operator()(...) -> bool { return false; }
};
} // namespace detail
template <typename Locale> typename Locale::id format_facet<Locale>::id;
@ -168,7 +145,7 @@ FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, basic_format_arg<format_context> 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

View File

@ -739,6 +739,21 @@ inline auto code_point_index(basic_string_view<char8_type> s, size_t n)
string_view(reinterpret_cast<const char*>(s.data()), s.size()), n);
}
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::value>;
template <typename T>
using is_integer =
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
#ifndef FMT_USE_FLOAT128
# ifdef __SIZEOF_FLOAT128__
# define FMT_USE_FLOAT128 1
@ -1017,15 +1032,6 @@ template <typename Locale> class format_facet : public Locale::facet {
FMT_BEGIN_DETAIL_NAMESPACE
template <typename T> struct is_integral : std::is_integral<T> {};
template <> struct is_integral<int128_opt> : std::true_type {};
template <> struct is_integral<uint128_t> : std::true_type {};
template <typename T>
using is_signed =
std::integral_constant<bool, std::numeric_limits<T>::is_signed ||
std::is_same<T, int128_opt>::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 <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
@ -1989,7 +1995,7 @@ template <typename Char> 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<Char> 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<format_context> value,
const format_specs& specs, locale_ref loc) -> bool;
template <typename OutputIt, typename Char>
inline auto write_loc(OutputIt, basic_format_arg<buffer_context<Char>>,
const basic_format_specs<Char>&, locale_ref) -> bool {
return false;
}
template <typename OutputIt, typename UInt, typename Char>
auto write_int(OutputIt& out, UInt value, unsigned prefix,
const basic_format_specs<Char>& specs, locale_ref loc) -> bool {
auto grouping = digit_grouping<Char>(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 <typename Char = char> struct loc_writer {
buffer_appender<Char> out;
const basic_format_specs<Char>& specs;
std::basic_string<Char> sep;
std::string grouping;
std::basic_string<Char> decimal_point;
template <typename T, FMT_ENABLE_IF(is_integer<T>::value)>
auto operator()(T value) -> bool {
auto arg = make_write_int_arg(value, specs.sign);
write_int(out, static_cast<uint64_or_128_t<T>>(arg.abs_value), arg.prefix,
specs, digit_grouping<Char>(grouping, sep));
return true;
}
template <typename T, FMT_ENABLE_IF(is_floating_point<T>::value)>
auto operator()(T) -> bool {
return false;
}
auto operator()(...) -> bool { return false; }
};
template <typename Char, typename OutputIt, typename T>
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt {
locale_ref) -> OutputIt {
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::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<uint64_or_128_t<T>>(abs_value), prefix,
specs, loc))
return out;
auto num_digits = count_digits(abs_value);
return write_int(
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
@ -2163,6 +2181,10 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt {
if (specs.localized &&
write_loc(out, make_arg<buffer_context<Char>>(value), specs, loc)) {
return out;
}
return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
loc);
}
@ -2174,6 +2196,10 @@ template <typename Char, typename OutputIt, typename T,
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
const basic_format_specs<Char>& specs,
locale_ref loc) -> OutputIt {
if (specs.localized &&
write_loc(out, make_arg<buffer_context<Char>>(value), specs, loc)) {
return out;
}
return write_int(out, make_write_int_arg(value, specs.sign), specs, loc);
}
@ -3455,12 +3481,6 @@ template <typename Char> struct custom_formatter {
template <typename T> void operator()(T) const {}
};
template <typename T>
using is_integer =
bool_constant<is_integral<T>::value && !std::is_same<T, bool>::value &&
!std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>;
template <typename ErrorHandler> class width_checker {
public:
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
@ -4171,10 +4191,6 @@ void vformat_to(buffer<Char>& buf, basic_string_view<Char> 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<Char>{context.out(), specs, context.locale()};
context.advance_to(visit_format_arg(f, arg));
return begin;

View File

@ -12,11 +12,32 @@
#include "format.h"
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
#endif
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
template <typename OutputIt>
auto write_loc(OutputIt out, basic_format_arg<buffer_context<wchar_t>> val,
const basic_format_specs<wchar_t>& specs, locale_ref loc)
-> bool {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto& numpunct =
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
auto separator = std::wstring();
auto grouping = numpunct.grouping();
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
return visit_format_arg(
loc_writer<wchar_t>{out, specs, separator, grouping, {}}, val);
#endif
return false;
}
} // namespace detail
FMT_MODULE_EXPORT_BEGIN

View File

@ -448,11 +448,11 @@ TEST(locale_test, int_formatter) {
auto f = fmt::formatter<int>();
auto parse_ctx = fmt::format_parse_context("L");
f.parse(parse_ctx);
char buf[10] = {};
fmt::basic_format_context<char*, char> 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<fmt::appender, char> 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