mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-30 02:37:36 +02:00
Improve locale handling
This commit is contained in:
@ -129,29 +129,6 @@ FMT_FUNC auto write_loc(appender out, basic_format_arg<format_context> value,
|
|||||||
#endif
|
#endif
|
||||||
return false;
|
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
|
} // namespace detail
|
||||||
|
|
||||||
template <typename Locale> typename Locale::id format_facet<Locale>::id;
|
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,
|
appender out, basic_format_arg<format_context> val,
|
||||||
const format_specs& specs) const -> bool {
|
const format_specs& specs) const -> bool {
|
||||||
return visit_format_arg(
|
return visit_format_arg(
|
||||||
detail::localizer{out, specs, separator_, grouping_, decimal_point_},
|
detail::loc_writer<>{out, specs, separator_, grouping_, decimal_point_},
|
||||||
val);
|
val);
|
||||||
}
|
}
|
||||||
#endif
|
#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);
|
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
|
#ifndef FMT_USE_FLOAT128
|
||||||
# ifdef __SIZEOF_FLOAT128__
|
# ifdef __SIZEOF_FLOAT128__
|
||||||
# define FMT_USE_FLOAT128 1
|
# define FMT_USE_FLOAT128 1
|
||||||
@ -1017,15 +1032,6 @@ template <typename Locale> class format_facet : public Locale::facet {
|
|||||||
|
|
||||||
FMT_BEGIN_DETAIL_NAMESPACE
|
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.
|
// Returns true if value is negative, false otherwise.
|
||||||
// Same as `value < 0` but doesn't produce warnings if T is an unsigned type.
|
// 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)>
|
template <typename T, FMT_ENABLE_IF(is_signed<T>::value)>
|
||||||
@ -1989,7 +1995,7 @@ template <typename Char> class digit_grouping {
|
|||||||
grouping_ = sep.grouping;
|
grouping_ = sep.grouping;
|
||||||
if (sep.thousands_sep) thousands_sep_.assign(1, sep.thousands_sep);
|
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)) {}
|
: grouping_(std::move(grouping)), thousands_sep_(std::move(sep)) {}
|
||||||
|
|
||||||
bool has_separator() const { return !thousands_sep_.empty(); }
|
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,
|
FMT_API auto write_loc(appender out, basic_format_arg<format_context> value,
|
||||||
const format_specs& specs, locale_ref loc) -> bool;
|
const format_specs& specs, locale_ref loc) -> bool;
|
||||||
|
|
||||||
template <typename OutputIt, typename Char>
|
template <typename OutputIt, typename Char>
|
||||||
inline auto write_loc(OutputIt, basic_format_arg<buffer_context<Char>>,
|
inline auto write_loc(OutputIt, basic_format_arg<buffer_context<Char>>,
|
||||||
const basic_format_specs<Char>&, locale_ref) -> bool {
|
const basic_format_specs<Char>&, locale_ref) -> bool {
|
||||||
return false;
|
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) {
|
FMT_CONSTEXPR inline void prefix_append(unsigned& prefix, unsigned value) {
|
||||||
prefix |= prefix != 0 ? value << 8 : value;
|
prefix |= prefix != 0 ? value << 8 : value;
|
||||||
prefix += (1u + (value > 0xff ? 1 : 0)) << 24;
|
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};
|
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>
|
template <typename Char, typename OutputIt, typename T>
|
||||||
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
|
||||||
const basic_format_specs<Char>& specs,
|
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, "");
|
static_assert(std::is_same<T, uint32_or_64_or_128_t<T>>::value, "");
|
||||||
auto abs_value = arg.abs_value;
|
auto abs_value = arg.abs_value;
|
||||||
auto prefix = arg.prefix;
|
auto prefix = arg.prefix;
|
||||||
switch (specs.type) {
|
switch (specs.type) {
|
||||||
case presentation_type::none:
|
case presentation_type::none:
|
||||||
case presentation_type::dec: {
|
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);
|
auto num_digits = count_digits(abs_value);
|
||||||
return write_int(
|
return write_int(
|
||||||
out, num_digits, prefix, specs, [=](reserve_iterator<OutputIt> it) {
|
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,
|
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
||||||
const basic_format_specs<Char>& specs,
|
const basic_format_specs<Char>& specs,
|
||||||
locale_ref loc) -> OutputIt {
|
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,
|
return write_int_noinline(out, make_write_int_arg(value, specs.sign), specs,
|
||||||
loc);
|
loc);
|
||||||
}
|
}
|
||||||
@ -2174,6 +2196,10 @@ template <typename Char, typename OutputIt, typename T,
|
|||||||
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value,
|
||||||
const basic_format_specs<Char>& specs,
|
const basic_format_specs<Char>& specs,
|
||||||
locale_ref loc) -> OutputIt {
|
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);
|
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> 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 {
|
template <typename ErrorHandler> class width_checker {
|
||||||
public:
|
public:
|
||||||
explicit FMT_CONSTEXPR width_checker(ErrorHandler& eh) : handler_(eh) {}
|
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);
|
begin = parse_format_specs(begin, end, handler);
|
||||||
if (begin == end || *begin != '}')
|
if (begin == end || *begin != '}')
|
||||||
on_error("missing '}' in format string");
|
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()};
|
auto f = arg_formatter<Char>{context.out(), specs, context.locale()};
|
||||||
context.advance_to(visit_format_arg(f, arg));
|
context.advance_to(visit_format_arg(f, arg));
|
||||||
return begin;
|
return begin;
|
||||||
|
@ -12,11 +12,32 @@
|
|||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
|
||||||
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
# include <locale>
|
||||||
|
#endif
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
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
|
FMT_MODULE_EXPORT_BEGIN
|
||||||
|
|
||||||
|
@ -448,11 +448,11 @@ TEST(locale_test, int_formatter) {
|
|||||||
auto f = fmt::formatter<int>();
|
auto f = fmt::formatter<int>();
|
||||||
auto parse_ctx = fmt::format_parse_context("L");
|
auto parse_ctx = fmt::format_parse_context("L");
|
||||||
f.parse(parse_ctx);
|
f.parse(parse_ctx);
|
||||||
char buf[10] = {};
|
auto buf = fmt::memory_buffer();
|
||||||
fmt::basic_format_context<char*, char> format_ctx(
|
fmt::basic_format_context<fmt::appender, char> format_ctx(
|
||||||
buf, {}, fmt::detail::locale_ref(loc));
|
fmt::appender(buf), {}, fmt::detail::locale_ref(loc));
|
||||||
*f.format(12345, format_ctx) = 0;
|
f.format(12345, format_ctx);
|
||||||
EXPECT_STREQ("12,345", buf);
|
EXPECT_EQ(fmt::to_string(buf), "12,345");
|
||||||
}
|
}
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
Reference in New Issue
Block a user