mirror of
https://github.com/fmtlib/fmt.git
synced 2025-07-31 03:07:36 +02:00
Switch to C++ locale
This commit is contained in:
36
fmt/format.h
36
fmt/format.h
@ -30,11 +30,11 @@
|
|||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <clocale>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
|
#include <locale>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
@ -645,6 +645,8 @@ class basic_buffer {
|
|||||||
|
|
||||||
T &operator[](std::size_t index) { return ptr_[index]; }
|
T &operator[](std::size_t index) { return ptr_[index]; }
|
||||||
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
const T &operator[](std::size_t index) const { return ptr_[index]; }
|
||||||
|
|
||||||
|
virtual const std::locale* locale() const { return 0; }
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
@ -962,18 +964,18 @@ struct no_thousands_sep {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// A functor that adds a thousands separator.
|
// A functor that adds a thousands separator.
|
||||||
|
template <typename Char>
|
||||||
class add_thousands_sep {
|
class add_thousands_sep {
|
||||||
private:
|
private:
|
||||||
fmt::string_view sep_;
|
fmt::basic_string_view<Char> sep_;
|
||||||
|
|
||||||
// Index of a decimal digit with the least significant digit having index 0.
|
// Index of a decimal digit with the least significant digit having index 0.
|
||||||
unsigned digit_index_;
|
unsigned digit_index_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit add_thousands_sep(fmt::string_view sep)
|
explicit add_thousands_sep(fmt::basic_string_view<Char> sep)
|
||||||
: sep_(sep), digit_index_(0) {}
|
: sep_(sep), digit_index_(0) {}
|
||||||
|
|
||||||
template <typename Char>
|
|
||||||
void operator()(Char *&buffer) {
|
void operator()(Char *&buffer) {
|
||||||
if (++digit_index_ % 3 != 0)
|
if (++digit_index_ % 3 != 0)
|
||||||
return;
|
return;
|
||||||
@ -1483,21 +1485,6 @@ basic_arg<Context> make_arg(const T &value) {
|
|||||||
return arg;
|
return arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T, T> struct lconv_check {
|
|
||||||
lconv_check(int) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Returns the thousands separator for the current locale.
|
|
||||||
// We check if ``lconv`` contains ``thousands_sep`` because on Android
|
|
||||||
// ``lconv`` is stubbed as an empty struct.
|
|
||||||
template <typename LConv>
|
|
||||||
inline string_view thousands_sep(
|
|
||||||
LConv *lc, lconv_check<char *LConv::*, &LConv::thousands_sep> = 0) {
|
|
||||||
return lc->thousands_sep;
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fmt::string_view thousands_sep(...) { return ""; }
|
|
||||||
|
|
||||||
#define FMT_CONCAT(a, b) a##b
|
#define FMT_CONCAT(a, b) a##b
|
||||||
|
|
||||||
#if FMT_GCC_VERSION >= 407
|
#if FMT_GCC_VERSION >= 407
|
||||||
@ -2323,7 +2310,7 @@ class basic_writer {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
\rst
|
\rst
|
||||||
Destroys a ``basic_writer`` object.
|
Destroys the ``basic_writer`` object.
|
||||||
\endrst
|
\endrst
|
||||||
*/
|
*/
|
||||||
virtual ~basic_writer() {}
|
virtual ~basic_writer() {}
|
||||||
@ -2629,12 +2616,17 @@ void basic_writer<Char>::write_int(T value, Spec spec) {
|
|||||||
}
|
}
|
||||||
case 'n': {
|
case 'n': {
|
||||||
unsigned num_digits = internal::count_digits(abs_value);
|
unsigned num_digits = internal::count_digits(abs_value);
|
||||||
fmt::string_view sep = internal::thousands_sep(std::localeconv());
|
const std::locale *loc = buffer_.locale();
|
||||||
|
if (!loc)
|
||||||
|
loc = &std::locale::classic();
|
||||||
|
Char thousands_sep =
|
||||||
|
std::use_facet<std::numpunct<Char>>(*loc).thousands_sep();
|
||||||
|
fmt::basic_string_view<Char> sep(&thousands_sep, 1);
|
||||||
unsigned size = static_cast<unsigned>(
|
unsigned size = static_cast<unsigned>(
|
||||||
num_digits + sep.size() * ((num_digits - 1) / 3));
|
num_digits + sep.size() * ((num_digits - 1) / 3));
|
||||||
CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
|
CharPtr p = prepare_int_buffer(size, spec, prefix, prefix_size) + 1;
|
||||||
internal::format_decimal(get(p), abs_value, 0,
|
internal::format_decimal(get(p), abs_value, 0,
|
||||||
internal::add_thousands_sep(sep));
|
internal::add_thousands_sep<Char>(sep));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -43,22 +43,6 @@
|
|||||||
// Test that the library compiles if None is defined to 0 as done by xlib.h.
|
// Test that the library compiles if None is defined to 0 as done by xlib.h.
|
||||||
#define None 0
|
#define None 0
|
||||||
|
|
||||||
struct LocaleMock {
|
|
||||||
static LocaleMock *instance;
|
|
||||||
|
|
||||||
MOCK_METHOD0(localeconv, lconv *());
|
|
||||||
} *LocaleMock::instance;
|
|
||||||
|
|
||||||
namespace fmt {
|
|
||||||
namespace std {
|
|
||||||
using namespace ::std;
|
|
||||||
lconv *localeconv() {
|
|
||||||
return LocaleMock::instance ?
|
|
||||||
LocaleMock::instance->localeconv() : ::std::localeconv();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#include "fmt/format.h"
|
#include "fmt/format.h"
|
||||||
|
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
@ -1115,14 +1099,9 @@ TEST(FormatterTest, FormatOct) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatIntLocale) {
|
TEST(FormatterTest, FormatIntLocale) {
|
||||||
ScopedMock<LocaleMock> mock;
|
|
||||||
lconv lc = {};
|
|
||||||
char sep[] = "--";
|
|
||||||
lc.thousands_sep = sep;
|
|
||||||
EXPECT_CALL(mock, localeconv()).Times(3).WillRepeatedly(testing::Return(&lc));
|
|
||||||
EXPECT_EQ("123", format("{:n}", 123));
|
EXPECT_EQ("123", format("{:n}", 123));
|
||||||
EXPECT_EQ("1--234", format("{:n}", 1234));
|
EXPECT_EQ("1,234", format("{:n}", 1234));
|
||||||
EXPECT_EQ("1--234--567", format("{:n}", 1234567));
|
EXPECT_EQ("1,234,567", format("{:n}", 1234567));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(FormatterTest, FormatFloat) {
|
TEST(FormatterTest, FormatFloat) {
|
||||||
|
@ -845,17 +845,3 @@ TEST(UtilTest, Conditional) {
|
|||||||
fmt::internal::conditional<false, int, char>::type *pc = &c;
|
fmt::internal::conditional<false, int, char>::type *pc = &c;
|
||||||
(void)pc;
|
(void)pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TestLConv {
|
|
||||||
char *thousands_sep;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct EmptyLConv {};
|
|
||||||
|
|
||||||
TEST(UtilTest, ThousandsSep) {
|
|
||||||
char foo[] = "foo";
|
|
||||||
TestLConv lc = {foo};
|
|
||||||
EXPECT_EQ("foo", fmt::internal::thousands_sep(&lc).to_string());
|
|
||||||
EmptyLConv empty_lc;
|
|
||||||
EXPECT_EQ("", fmt::internal::thousands_sep(&empty_lc));
|
|
||||||
}
|
|
||||||
|
Reference in New Issue
Block a user