Simplify locale handling

This commit is contained in:
Victor Zverovich 2024-08-29 13:04:56 -07:00
parent cd8d01d8cd
commit e582d377c2
2 changed files with 49 additions and 61 deletions

View File

@ -14,10 +14,6 @@
# include <climits> # include <climits>
# include <cmath> # include <cmath>
# include <exception> # include <exception>
# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# include <locale>
# endif
#endif #endif
#if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE)
@ -26,6 +22,19 @@
#include "format.h" #include "format.h"
#ifdef FMT_USE_LOCALE
// Use the provided definition.
#elif defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# define FMT_USE_LOCALE 0
#else
# define FMT_USE_LOCALE 1
#endif
#if FMT_USE_LOCALE
# include <locale>
#elif !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
# define FMT_STATIC_THOUSANDS_SEPARATOR ','
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
@ -77,53 +86,56 @@ inline void fwrite_fully(const void* ptr, size_t count, FILE* stream) {
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file"))); FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
} }
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR #if FMT_USE_LOCALE
using std::locale;
using std::numpunct;
using std::use_facet;
#else
struct locale {};
template <typename Char> struct numpunct {
auto grouping() const -> std::string { return "\03"; }
auto thousands_sep() const -> Char { return FMT_STATIC_THOUSANDS_SEPARATOR; }
auto decimal_point() const -> Char { return '.'; }
};
template <typename Facet> Facet use_facet(locale) { return {}; }
#endif // FMT_USE_LOCALE
template <typename Locale> template <typename Locale>
locale_ref::locale_ref(const Locale& loc) : locale_(&loc) { locale_ref::locale_ref(const Locale& loc) : locale_(&loc) {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, locale>::value, "");
} }
template <typename Locale> auto locale_ref::get() const -> Locale { template <typename Locale> auto locale_ref::get() const -> Locale {
static_assert(std::is_same<Locale, std::locale>::value, ""); static_assert(std::is_same<Locale, locale>::value, "");
return locale_ ? *static_cast<const std::locale*>(locale_) : std::locale(); return locale_ ? *static_cast<const locale*>(locale_) : locale();
} }
template <typename Char> template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> { FMT_FUNC auto thousands_sep_impl(locale_ref loc) -> thousands_sep_result<Char> {
auto& facet = std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()); auto&& facet = use_facet<numpunct<Char>>(loc.get<locale>());
auto grouping = facet.grouping(); auto grouping = facet.grouping();
auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep(); auto thousands_sep = grouping.empty() ? Char() : facet.thousands_sep();
return {std::move(grouping), thousands_sep}; return {std::move(grouping), thousands_sep};
} }
template <typename Char> template <typename Char>
FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char { FMT_FUNC auto decimal_point_impl(locale_ref loc) -> Char {
return std::use_facet<std::numpunct<Char>>(loc.get<std::locale>()) return use_facet<numpunct<Char>>(loc.get<locale>()).decimal_point();
.decimal_point();
} }
#else
template <typename Char>
FMT_FUNC auto thousands_sep_impl(locale_ref) -> thousands_sep_result<Char> {
return {"\03", FMT_STATIC_THOUSANDS_SEPARATOR};
}
template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.';
}
#endif
FMT_FUNC auto write_loc(appender out, loc_value value, FMT_FUNC auto write_loc(appender out, loc_value value,
const format_specs& specs, locale_ref loc) -> bool { const format_specs& specs, locale_ref loc) -> bool {
#ifdef FMT_STATIC_THOUSANDS_SEPARATOR #if FMT_USE_LOCALE
value.visit(loc_writer<>{
out, specs, std::string(1, FMT_STATIC_THOUSANDS_SEPARATOR), "\3", "."});
return true;
#else
auto locale = loc.get<std::locale>(); auto locale = loc.get<std::locale>();
// We cannot use the num_put<char> facet because it may produce output in // We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding. // a wrong encoding.
using facet = format_facet<std::locale>; using facet = format_facet<std::locale>;
if (std::has_facet<facet>(locale)) if (std::has_facet<facet>(locale))
return std::use_facet<facet>(locale).put(out, value, specs); return use_facet<facet>(locale).put(out, value, specs);
return facet(locale).put(out, value, specs); return facet(locale).put(out, value, specs);
#else
value.visit(loc_writer<>{
out, specs, std::string(1, FMT_STATIC_THOUSANDS_SEPARATOR), "\3", "."});
return true;
#endif #endif
} }
} // namespace detail } // namespace detail
@ -134,13 +146,13 @@ FMT_FUNC void report_error(const char* message) {
template <typename Locale> typename Locale::id format_facet<Locale>::id; template <typename Locale> typename Locale::id format_facet<Locale>::id;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) { template <typename Locale> format_facet<Locale>::format_facet(Locale& loc) {
auto& numpunct = std::use_facet<std::numpunct<char>>(loc); auto& np = detail::use_facet<detail::numpunct<char>>(loc);
grouping_ = numpunct.grouping(); grouping_ = np.grouping();
if (!grouping_.empty()) separator_ = std::string(1, numpunct.thousands_sep()); if (!grouping_.empty()) separator_ = std::string(1, np.thousands_sep());
} }
#if FMT_USE_LOCALE
template <> template <>
FMT_API FMT_FUNC auto format_facet<std::locale>::do_put( FMT_API FMT_FUNC auto format_facet<std::locale>::do_put(
appender out, loc_value val, const format_specs& specs) const -> bool { appender out, loc_value val, const format_specs& specs) const -> bool {
@ -1019,7 +1031,8 @@ template <> struct cache_accessor<double> {
{0xe4d5e82392a40515, 0x0fabaf3feaa5334b}, {0xe4d5e82392a40515, 0x0fabaf3feaa5334b},
{0xb8da1662e7b00a17, 0x3d6a751f3b936244}, {0xb8da1662e7b00a17, 0x3d6a751f3b936244},
{0x95527a5202df0ccb, 0x0f37801e0c43ebc9}, {0x95527a5202df0ccb, 0x0f37801e0c43ebc9},
{0xf13e34aabb430a15, 0x647726b9e7c68ff0} { 0xf13e34aabb430a15,
0x647726b9e7c68ff0 }
#endif #endif
}; };

View File

@ -765,16 +765,6 @@ using is_integer =
!std::is_same<T, char>::value && !std::is_same<T, char>::value &&
!std::is_same<T, wchar_t>::value>; !std::is_same<T, wchar_t>::value>;
#ifndef FMT_USE_FLOAT
# define FMT_USE_FLOAT 1
#endif
#ifndef FMT_USE_DOUBLE
# define FMT_USE_DOUBLE 1
#endif
#ifndef FMT_USE_LONG_DOUBLE
# define FMT_USE_LONG_DOUBLE 1
#endif
#if defined(FMT_USE_FLOAT128) #if defined(FMT_USE_FLOAT128)
// Use the provided definition. // Use the provided definition.
#elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE(<quadmath.h>) #elif FMT_CLANG_VERSION && FMT_HAS_INCLUDE(<quadmath.h>)
@ -1132,14 +1122,6 @@ constexpr auto is_negative(T) -> bool {
return false; return false;
} }
template <typename T>
FMT_CONSTEXPR auto is_supported_floating_point(T) -> bool {
if (std::is_same<T, float>()) return FMT_USE_FLOAT;
if (std::is_same<T, double>()) return FMT_USE_DOUBLE;
if (std::is_same<T, long double>()) return FMT_USE_LONG_DOUBLE;
return true;
}
// Smallest of uint32_t, uint64_t, uint128_t that is large enough to // Smallest of uint32_t, uint64_t, uint128_t that is large enough to
// represent all values of an integral type T. // represent all values of an integral type T.
template <typename T> template <typename T>
@ -3556,7 +3538,6 @@ template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_floating_point<T>::value)> FMT_ENABLE_IF(is_floating_point<T>::value)>
FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs, FMT_CONSTEXPR20 auto write(OutputIt out, T value, format_specs specs,
locale_ref loc = {}) -> OutputIt { locale_ref loc = {}) -> OutputIt {
if (const_check(!is_supported_floating_point(value))) return out;
return specs.localized() && write_loc(out, value, specs, loc) return specs.localized() && write_loc(out, value, specs, loc)
? out ? out
: write_float<Char>(out, value, specs, loc); : write_float<Char>(out, value, specs, loc);
@ -3566,7 +3547,6 @@ template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(is_fast_float<T>::value)> FMT_ENABLE_IF(is_fast_float<T>::value)>
FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt { FMT_CONSTEXPR20 auto write(OutputIt out, T value) -> OutputIt {
if (is_constant_evaluated()) return write<Char>(out, value, format_specs()); if (is_constant_evaluated()) return write<Char>(out, value, format_specs());
if (const_check(!is_supported_floating_point(value))) return out;
auto s = detail::signbit(value) ? sign::minus : sign::none; auto s = detail::signbit(value) ? sign::minus : sign::none;
@ -3708,28 +3688,23 @@ template <typename Char> struct default_arg_formatter {
}; };
template <typename Char> struct arg_formatter { template <typename Char> struct arg_formatter {
using iterator = basic_appender<Char>; basic_appender<Char> out;
using context = buffered_context<Char>;
iterator out;
const format_specs& specs; const format_specs& specs;
locale_ref locale; locale_ref locale;
template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)> template <typename T, FMT_ENABLE_IF(is_builtin<T>::value)>
FMT_CONSTEXPR FMT_INLINE auto operator()(T value) -> iterator { FMT_CONSTEXPR FMT_INLINE void operator()(T value) {
return detail::write<Char>(out, value, specs, locale); detail::write<Char>(out, value, specs, locale);
} }
template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)> template <typename T, FMT_ENABLE_IF(!is_builtin<T>::value)>
auto operator()(T) -> iterator { void operator()(T) {
FMT_ASSERT(false, ""); FMT_ASSERT(false, "");
return out;
} }
auto operator()(typename basic_format_arg<context>::handle) -> iterator { void operator()(typename basic_format_arg<buffered_context<Char>>::handle) {
// User-defined types are handled separately because they require access // User-defined types are handled separately because they require access
// to the parse context. // to the parse context.
return out;
} }
}; };