mirror of
https://github.com/fmtlib/fmt.git
synced 2025-01-26 12:35:32 +00:00
Improve locale handling
This commit is contained in:
parent
bac53951b8
commit
b98ffb7dbd
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user