Improve locale support

This commit is contained in:
Victor Zverovich
2022-08-31 08:05:17 -07:00
parent 0b0f7cfbfc
commit 64e29893cf
5 changed files with 112 additions and 13 deletions

View File

@@ -17,6 +17,7 @@
#include <cstring> // std::memmove
#include <cwchar>
#include <exception>
#include <sstream>
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
# include <locale>
@@ -27,8 +28,11 @@
#endif
#include "format.h"
#include "locale.h"
FMT_BEGIN_NAMESPACE
template <typename Char> std::locale::id num_format_facet<Char>::id;
namespace detail {
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
@@ -115,6 +119,28 @@ template <typename Char> FMT_FUNC Char decimal_point_impl(locale_ref) {
return '.';
}
#endif
template <typename Char>
FMT_FUNC auto write_int(unsigned long long value, locale_ref loc)
-> std::basic_string<Char> {
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
auto&& ios = std::basic_ios<Char>(nullptr);
auto locale = loc.get<std::locale>();
ios.imbue(locale);
auto&& buf = std::basic_stringbuf<Char>();
auto out = std::ostreambuf_iterator<Char>(&buf);
// We cannot use the num_put<char> facet because it may produce output in
// a wrong encoding.
using facet_t = conditional_t<std::is_same<Char, char>::value,
num_format_facet<>, std::num_put<Char>>;
if (std::has_facet<facet_t>(locale)) {
std::use_facet<facet_t>(locale).put(out, ios, ' ', value);
return buf.str();
}
#endif
return {};
}
} // namespace detail
#if !FMT_MSC_VERSION

View File

@@ -1992,10 +1992,11 @@ template <typename Char> class digit_grouping {
}
};
// Writes a decimal integer with digit grouping.
template <typename OutputIt, typename UInt, typename Char>
auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
const basic_format_specs<Char>& specs,
const digit_grouping<Char>& grouping) -> OutputIt {
auto write_int(OutputIt out, UInt value, unsigned prefix,
const basic_format_specs<Char>& specs,
const digit_grouping<Char>& grouping) -> OutputIt {
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
int num_digits = count_digits(value);
char digits[40];
@@ -2012,12 +2013,32 @@ auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
});
}
template <typename Char>
FMT_API auto write_int(unsigned long long value, locale_ref loc)
-> std::basic_string<Char>;
template <typename OutputIt, typename UInt, typename Char>
auto write_int_localized(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_localized(out, value, prefix, specs, grouping);
auto write_int(OutputIt& out, UInt value, unsigned prefix,
const basic_format_specs<Char>& specs, locale_ref loc) -> bool {
using char_t =
conditional_t<std::is_same<Char, wchar_t>::value, wchar_t, char>;
auto str = std::basic_string<char_t>();
if (sizeof(value) <= sizeof(unsigned long long))
str = write_int<char_t>(static_cast<unsigned long long>(value), loc);
if (str.empty()) {
auto grouping = digit_grouping<Char>(loc);
out = write_int(out, value, prefix, specs, grouping);
return true;
}
size_t size = to_unsigned((prefix != 0 ? 1 : 0) + str.size());
out = write_padded<align::right>(
out, specs, size, size, [&](reserve_iterator<OutputIt> it) {
if (prefix != 0) {
char sign = static_cast<char>(prefix);
*it++ = static_cast<Char>(sign);
}
return copy_str<Char>(str.data(), str.data() + str.size(), it);
});
return true;
}
@@ -2058,10 +2079,9 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg<T> arg,
case presentation_type::none:
case presentation_type::dec: {
if (specs.localized &&
write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
prefix, specs, loc)) {
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) {
@@ -3914,7 +3934,7 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
specs_.width_ref, ctx);
detail::handle_dynamic_spec<detail::precision_checker>(
specs_.precision, specs_.precision_ref, ctx);
return detail::write_int_localized(
return detail::write_int(
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
detail::digit_grouping<char>({"\3", ','}));
}

26
include/fmt/locale.h Normal file
View File

@@ -0,0 +1,26 @@
// Formatting library for C++ - optional locale support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale> // std::num_put
#include "core.h"
FMT_BEGIN_NAMESPACE
// A locale facet that formats numeric values in UTF-8.
template <typename Char = char>
class num_format_facet : public std::num_put<Char> {
public:
static FMT_API std::locale::id id;
};
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_