mirror of
https://github.com/fmtlib/fmt.git
synced 2025-04-10 12:44:48 +00:00
Improve locale support
This commit is contained in:
parent
0b0f7cfbfc
commit
64e29893cf
@ -17,6 +17,7 @@
|
|||||||
#include <cstring> // std::memmove
|
#include <cstring> // std::memmove
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
# include <locale>
|
# include <locale>
|
||||||
@ -27,8 +28,11 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "format.h"
|
#include "format.h"
|
||||||
|
#include "locale.h"
|
||||||
|
|
||||||
FMT_BEGIN_NAMESPACE
|
FMT_BEGIN_NAMESPACE
|
||||||
|
template <typename Char> std::locale::id num_format_facet<Char>::id;
|
||||||
|
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
|
||||||
FMT_FUNC void assert_fail(const char* file, int line, const char* message) {
|
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 '.';
|
return '.';
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
} // namespace detail
|
||||||
|
|
||||||
#if !FMT_MSC_VERSION
|
#if !FMT_MSC_VERSION
|
||||||
|
@ -1992,10 +1992,11 @@ template <typename Char> class digit_grouping {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Writes a decimal integer with digit grouping.
|
||||||
template <typename OutputIt, typename UInt, typename Char>
|
template <typename OutputIt, typename UInt, typename Char>
|
||||||
auto write_int_localized(OutputIt out, UInt value, unsigned prefix,
|
auto write_int(OutputIt out, UInt value, unsigned prefix,
|
||||||
const basic_format_specs<Char>& specs,
|
const basic_format_specs<Char>& specs,
|
||||||
const digit_grouping<Char>& grouping) -> OutputIt {
|
const digit_grouping<Char>& grouping) -> OutputIt {
|
||||||
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
|
static_assert(std::is_same<uint64_or_128_t<UInt>, UInt>::value, "");
|
||||||
int num_digits = count_digits(value);
|
int num_digits = count_digits(value);
|
||||||
char digits[40];
|
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>
|
template <typename OutputIt, typename UInt, typename Char>
|
||||||
auto write_int_localized(OutputIt& out, UInt value, unsigned prefix,
|
auto write_int(OutputIt& out, UInt value, unsigned prefix,
|
||||||
const basic_format_specs<Char>& specs, locale_ref loc)
|
const basic_format_specs<Char>& specs, locale_ref loc) -> bool {
|
||||||
-> bool {
|
using char_t =
|
||||||
auto grouping = digit_grouping<Char>(loc);
|
conditional_t<std::is_same<Char, wchar_t>::value, wchar_t, char>;
|
||||||
out = write_int_localized(out, value, prefix, specs, grouping);
|
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;
|
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::none:
|
||||||
case presentation_type::dec: {
|
case presentation_type::dec: {
|
||||||
if (specs.localized &&
|
if (specs.localized &&
|
||||||
write_int_localized(out, static_cast<uint64_or_128_t<T>>(abs_value),
|
write_int(out, static_cast<uint64_or_128_t<T>>(abs_value), prefix,
|
||||||
prefix, specs, loc)) {
|
specs, loc))
|
||||||
return out;
|
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) {
|
||||||
@ -3914,7 +3934,7 @@ template <typename T> struct formatter<group_digits_view<T>> : formatter<T> {
|
|||||||
specs_.width_ref, ctx);
|
specs_.width_ref, ctx);
|
||||||
detail::handle_dynamic_spec<detail::precision_checker>(
|
detail::handle_dynamic_spec<detail::precision_checker>(
|
||||||
specs_.precision, specs_.precision_ref, ctx);
|
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_,
|
ctx.out(), static_cast<detail::uint64_or_128_t<T>>(t.value), 0, specs_,
|
||||||
detail::digit_grouping<char>({"\3", ','}));
|
detail::digit_grouping<char>({"\3", ','}));
|
||||||
}
|
}
|
||||||
|
26
include/fmt/locale.h
Normal file
26
include/fmt/locale.h
Normal 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_
|
@ -22,6 +22,9 @@ template FMT_API auto locale_ref::get<std::locale>() const -> std::locale;
|
|||||||
|
|
||||||
// Explicit instantiations for char.
|
// Explicit instantiations for char.
|
||||||
|
|
||||||
|
template FMT_API auto write_int(unsigned long long, locale_ref)
|
||||||
|
-> std::basic_string<char>;
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||||
-> thousands_sep_result<char>;
|
-> thousands_sep_result<char>;
|
||||||
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
template FMT_API auto decimal_point_impl(locale_ref) -> char;
|
||||||
@ -37,6 +40,9 @@ template FMT_API void vformat_to(buffer<char>&, string_view,
|
|||||||
|
|
||||||
// Explicit instantiations for wchar_t.
|
// Explicit instantiations for wchar_t.
|
||||||
|
|
||||||
|
template FMT_API auto write_int(unsigned long long, locale_ref)
|
||||||
|
-> std::basic_string<wchar_t>;
|
||||||
|
|
||||||
template FMT_API auto thousands_sep_impl(locale_ref)
|
template FMT_API auto thousands_sep_impl(locale_ref)
|
||||||
-> thousands_sep_result<wchar_t>;
|
-> thousands_sep_result<wchar_t>;
|
||||||
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t;
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
|
|
||||||
#include "fmt/xchar.h"
|
#include "fmt/xchar.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
#include <cwchar>
|
#include <cwchar>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "fmt/chrono.h"
|
#include "fmt/chrono.h"
|
||||||
#include "fmt/color.h"
|
#include "fmt/color.h"
|
||||||
|
#include "fmt/locale.h"
|
||||||
#include "fmt/ostream.h"
|
#include "fmt/ostream.h"
|
||||||
#include "fmt/ranges.h"
|
#include "fmt/ranges.h"
|
||||||
#include "gtest-extra.h" // Contains
|
#include "gtest-extra.h" // Contains
|
||||||
@ -344,6 +346,7 @@ TEST(xchar_test, escape_string) {
|
|||||||
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
|
TEST(xchar_test, to_wstring) { EXPECT_EQ(L"42", fmt::to_wstring(42)); }
|
||||||
|
|
||||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
|
||||||
template <typename Char> struct numpunct : std::numpunct<Char> {
|
template <typename Char> struct numpunct : std::numpunct<Char> {
|
||||||
protected:
|
protected:
|
||||||
Char do_decimal_point() const override { return '?'; }
|
Char do_decimal_point() const override { return '?'; }
|
||||||
@ -441,7 +444,7 @@ TEST(locale_test, wformat) {
|
|||||||
fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
|
fmt::format(small_grouping_loc, L"{:L}", max_value<uint32_t>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(locale_test, double_formatter) {
|
TEST(locale_test, int_formatter) {
|
||||||
auto loc = std::locale(std::locale(), new special_grouping<char>());
|
auto loc = std::locale(std::locale(), new special_grouping<char>());
|
||||||
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");
|
||||||
@ -518,4 +521,22 @@ TEST(locale_test, sign) {
|
|||||||
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
|
EXPECT_EQ(fmt::format(std::locale(), L"{:L}", -50), L"-50");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class num_format : public fmt::num_format_facet<> {
|
||||||
|
protected:
|
||||||
|
using fmt::num_format_facet<>::do_put;
|
||||||
|
iter_type do_put(iter_type out, std::ios_base&, char,
|
||||||
|
unsigned long long) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
num_format::iter_type num_format::do_put(iter_type out, std::ios_base&, char,
|
||||||
|
unsigned long long) const {
|
||||||
|
const char s[] = "foo";
|
||||||
|
return std::copy_n(s, sizeof(s) - 1, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(locale_test, num_format) {
|
||||||
|
auto loc = std::locale(std::locale(), new num_format());
|
||||||
|
EXPECT_EQ(fmt::format(loc, "{:L}", 42), "foo");
|
||||||
|
}
|
||||||
|
|
||||||
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
#endif // FMT_STATIC_THOUSANDS_SEPARATOR
|
||||||
|
Loading…
x
Reference in New Issue
Block a user