Switch to C++ locale

This commit is contained in:
Victor Zverovich
2017-03-25 08:20:06 -07:00
parent b4f4b7e21a
commit 32ec13f149
3 changed files with 16 additions and 59 deletions

View File

@ -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:

View File

@ -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) {

View File

@ -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));
}