Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom boost::locale::numpunct #170

Draft
wants to merge 5 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ jobs:

gen_locale en_US.UTF-8 # Assumed to be there by tests
# Used by various tests
gen_locale de_DE.UTF-8
gen_locale he_IL.UTF-8
gen_locale ja_JP.UTF-8
gen_locale ru_RU.UTF-8
Expand Down
1 change: 1 addition & 0 deletions include/boost/locale.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include <boost/locale/info.hpp>
#include <boost/locale/localization_backend.hpp>
#include <boost/locale/message.hpp>
#include <boost/locale/numpunct.hpp>
#include <boost/locale/util.hpp>
#include <boost/locale/util/locale_data.hpp>

Expand Down
87 changes: 87 additions & 0 deletions include/boost/locale/numpunct.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
//
// Copyright (c) 2021-2021 Salvo Miosi
// Copyright (c) 2023-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//

#ifndef BOOST_LOCALE_NUMPUNCT_HPP_INCLUDED
#define BOOST_LOCALE_NUMPUNCT_HPP_INCLUDED

#include <boost/locale/config.hpp>
#include <boost/locale/detail/is_supported_char.hpp>
#include <locale>
#include <string>

namespace boost { namespace locale {

/// \brief Extension of `std::numpunct` providing possibly encoded values of decimal point and thousands separator.
///
/// To achieve interface compatibility with `std::numpunct` for the case where the separators are encoded using
/// multiple chars the functions `do_decimal_point` and `do_thousands_sep` will fall back to the values used by the
/// "C" locale.
///
/// \note
///
/// - Not all backends support encoded separators, so \ref decimal_point_str & \ref thousands_sep_str may return
/// strings of length 1.
/// - Some backends may provide single char replacements of the encoded separators instead of falling back to the
/// "C" locale.
template<typename CharType>
class numpunct : public std::numpunct<CharType> {
BOOST_LOCALE_ASSERT_IS_SUPPORTED(CharType);

public:
using string_type = std::numpunct<CharType>::string_type;

numpunct(size_t refs = 0) : std::numpunct<CharType>(refs) {}

/// Provides the character to use as decimal point possibly encoded into multiple code units
string_type decimal_point_str() const { return do_decimal_point_str(); }
/// Provides the character to use as thousands separator possibly encoded into multiple code units
string_type thousands_sep_str() const { return do_thousands_sep_str(); }

protected:
CharType do_decimal_point() const override
{
const string_type dec = do_decimal_point_str();
return (dec.size() > 1) ? '.' : dec[0];
}

/// Provides the character to use as decimal point possibly encoded into multiple code units
virtual string_type do_decimal_point_str() const
{
static const char t[] = ".";
return string_type(t, t + sizeof(t) - 1);
}

CharType do_thousands_sep() const override
{
const string_type sep = do_thousands_sep_str();
return (sep.size() > 1) ? '.' : sep[0];
}

/// Provides the character to use as thousands separator possibly encoded into multiple code units
virtual string_type do_thousands_sep_str() const
{
static const char t[] = ",";
return string_type(t, t + sizeof(t) - 1);
}

string_type do_truename() const override
{
static const char t[] = "true";
return string_type(t, t + sizeof(t) - 1);
}

string_type do_falsename() const override
{
static const char t[] = "false";
return string_type(t, t + sizeof(t) - 1);
}
};
}} // namespace boost::locale

#endif
43 changes: 43 additions & 0 deletions src/boost/locale/icu/numeric.cpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,26 @@
//
// Copyright (c) 2009-2011 Artyom Beilis (Tonkikh)
// Copyright (c) 2021-2021 Salvo Miosi
// Copyright (c) 2022-2023 Alexander Grund
//
// Distributed under the Boost Software License, Version 1.0.
// https://www.boost.org/LICENSE_1_0.txt

#include <boost/locale/formatting.hpp>
#include <boost/locale/numpunct.hpp>
#include "boost/locale/icu/all_generator.hpp"
#include "boost/locale/icu/cdata.hpp"
#include "boost/locale/icu/formatter.hpp"
#include "boost/locale/icu/formatters_cache.hpp"
#include "boost/locale/icu/uconv.hpp"
#include <algorithm>
#include <ios>
#include <limits>
#include <locale>
#include <string>
#include <type_traits>
#include <unicode/decimfmt.h>
#include <unicode/numfmt.h>

namespace boost { namespace locale { namespace impl_icu {

Expand Down Expand Up @@ -304,12 +310,49 @@ namespace boost { namespace locale { namespace impl_icu {
std::string enc_;
};

template<typename CharType>
struct icu_numpunct : public numpunct<CharType> {
typedef std::basic_string<CharType> string_type;

public:
icu_numpunct(const cdata& d)
{
UErrorCode err = U_ZERO_ERROR;
icu::NumberFormat* fmt = icu::NumberFormat::createInstance(d.locale(), UNUM_DECIMAL, err);
if(icu::DecimalFormat* dec = icu_cast<icu::DecimalFormat>(fmt)) {
boost::locale::impl_icu::icu_std_converter<CharType> cnv(d.encoding());
const icu::DecimalFormatSymbols* syms = dec->getDecimalFormatSymbols();
decimal_point_ = cnv.std(syms->getSymbol(icu::DecimalFormatSymbols::kDecimalSeparatorSymbol));
thousands_sep_ = cnv.std(syms->getSymbol(icu::DecimalFormatSymbols::kGroupingSeparatorSymbol));
if(dec->isGroupingUsed()) {
int32_t grouping_size = dec->getGroupingSize();
grouping_ = std::string(reinterpret_cast<char*>(&grouping_size), 1);
int32_t grouping_size_2 = dec->getSecondaryGroupingSize();
if(grouping_size_2 > 0 && grouping_size_2 != grouping_size) {
grouping_ += static_cast<char>(grouping_size_2);
}
}
}
}

protected:
string_type do_decimal_point_str() const override { return decimal_point_; }
string_type do_thousands_sep_str() const override { return thousands_sep_; }
std::string do_grouping() const override { return grouping_; }

private:
string_type decimal_point_;
string_type thousands_sep_;
std::string grouping_;
};

template<typename CharType>
std::locale install_formatting_facets(const std::locale& in, const cdata& cd)
{
std::locale tmp = std::locale(in, new num_format<CharType>(cd));
if(!std::has_facet<formatters_cache>(in))
tmp = std::locale(tmp, new formatters_cache(cd.locale()));
tmp = std::locale(tmp, new icu_numpunct<CharType>(cd));
return tmp;
}

Expand Down
23 changes: 5 additions & 18 deletions src/boost/locale/posix/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <boost/locale/encoding.hpp>
#include <boost/locale/formatting.hpp>
#include <boost/locale/generator.hpp>
#include <boost/locale/numpunct.hpp>
#include <boost/predef/os.h>
#include <algorithm>
#include <cctype>
Expand Down Expand Up @@ -341,38 +342,24 @@ namespace boost { namespace locale { namespace impl_posix {
};

template<typename CharType>
class num_punct_posix : public std::numpunct<CharType> {
class num_punct_posix : public numpunct<CharType> {
public:
typedef std::basic_string<CharType> string_type;
num_punct_posix(locale_t lc, size_t refs = 0) : std::numpunct<CharType>(refs)
num_punct_posix(locale_t lc, size_t refs = 0) : numpunct<CharType>(refs)
{
basic_numpunct np(lc);
to_str(np.thousands_sep, thousands_sep_, lc);
to_str(np.decimal_point, decimal_point_, lc);
grouping_ = np.grouping;
if(thousands_sep_.size() > 1)
grouping_ = std::string();
if(decimal_point_.size() > 1)
decimal_point_ = CharType('.');
}
void to_str(std::string& s1, std::string& s2, locale_t /*lc*/) { s2.swap(s1); }
void to_str(std::string& s1, std::wstring& s2, locale_t lc)
{
s2 = conv::to_utf<wchar_t>(s1, nl_langinfo_l(CODESET, lc));
}
CharType do_decimal_point() const override { return *decimal_point_.c_str(); }
CharType do_thousands_sep() const override { return *thousands_sep_.c_str(); }
string_type do_decimal_point_str() const override { return decimal_point_; }
string_type do_thousands_sep_str() const override { return thousands_sep_; }
std::string do_grouping() const override { return grouping_; }
string_type do_truename() const override
{
static const char t[] = "true";
return string_type(t, t + sizeof(t) - 1);
}
string_type do_falsename() const override
{
static const char t[] = "false";
return string_type(t, t + sizeof(t) - 1);
}

private:
string_type decimal_point_;
Expand Down
41 changes: 12 additions & 29 deletions src/boost/locale/win32/numeric.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <boost/locale/encoding.hpp>
#include <boost/locale/formatting.hpp>
#include <boost/locale/generator.hpp>
#include <boost/locale/numpunct.hpp>
#include "boost/locale/win32/all_generator.hpp"
#include "boost/locale/win32/api.hpp"
#include <algorithm>
Expand Down Expand Up @@ -96,10 +97,10 @@ namespace boost { namespace locale { namespace impl_win {
};

template<typename CharType>
class num_punct_win : public std::numpunct<CharType> {
class num_punct_win : public numpunct<CharType> {
public:
typedef std::basic_string<CharType> string_type;
num_punct_win(const winlocale& lc, size_t refs = 0) : std::numpunct<CharType>(refs)
num_punct_win(const winlocale& lc, size_t refs = 0) : numpunct<CharType>(refs)
{
numeric_info np = wcsnumformat_l(lc);

Expand All @@ -111,28 +112,14 @@ namespace boost { namespace locale { namespace impl_win {
to_str(np.thousands_sep, thousands_sep_);
to_str(np.decimal_point, decimal_point_);
grouping_ = np.grouping;
if(thousands_sep_.size() > 1)
grouping_ = std::string();
if(decimal_point_.size() > 1)
decimal_point_ = CharType('.');
}

void to_str(std::wstring& s1, std::wstring& s2) { s2.swap(s1); }

void to_str(std::wstring& s1, std::string& s2) { s2 = conv::utf_to_utf<char>(s1); }
CharType do_decimal_point() const override { return *decimal_point_.c_str(); }
CharType do_thousands_sep() const override { return *thousands_sep_.c_str(); }
string_type do_decimal_point_str() const override { return decimal_point_; }
string_type do_thousands_sep_str() const override { return thousands_sep_; }
std::string do_grouping() const override { return grouping_; }
string_type do_truename() const override
{
static const char t[] = "true";
return string_type(t, t + sizeof(t) - 1);
}
string_type do_falsename() const override
{
static const char t[] = "false";
return string_type(t, t + sizeof(t) - 1);
}

private:
string_type decimal_point_;
Expand All @@ -143,29 +130,25 @@ namespace boost { namespace locale { namespace impl_win {
template<typename CharType>
std::locale create_formatting_impl(const std::locale& in, const winlocale& lc)
{
std::locale tmp(in, new num_format<CharType>(lc));
if(lc.is_c()) {
std::locale tmp(in, new std::numpunct_byname<CharType>("C"));
tmp = std::locale(tmp, new numpunct<CharType>());
tmp = std::locale(tmp, new std::time_put_byname<CharType>("C"));
tmp = std::locale(tmp, new num_format<CharType>(lc));
return tmp;
} else {
std::locale tmp(in, new num_punct_win<CharType>(lc));
tmp = std::locale(tmp, new num_punct_win<CharType>(lc));
tmp = std::locale(tmp, new time_put_win<CharType>(lc));
tmp = std::locale(tmp, new num_format<CharType>(lc));
return tmp;
}
return tmp;
}

template<typename CharType>
std::locale create_parsing_impl(const std::locale& in, const winlocale& lc)
{
std::numpunct<CharType>* np = 0;
std::locale tmp(in, new util::base_num_parse<CharType>());
if(lc.is_c())
np = new std::numpunct_byname<CharType>("C");
tmp = std::locale(tmp, new numpunct<CharType>());
else
np = new num_punct_win<CharType>(lc);
std::locale tmp(in, np);
tmp = std::locale(tmp, new util::base_num_parse<CharType>());
tmp = std::locale(tmp, new num_punct_win<CharType>(lc));
return tmp;
}

Expand Down
1 change: 1 addition & 0 deletions test/Jamfile.v2
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ run test_codecvt.cpp ;
run test_codepage_converter.cpp ;
run test_stream_io.cpp ;
run test_message.cpp : $(BOOST_ROOT)/libs/locale/test ;
run test_numpunct.cpp ;
run test_generator.cpp ;
# icu
run test_collate.cpp ;
Expand Down
1 change: 1 addition & 0 deletions test/test_generator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ void test_main(int /*argc*/, char** /*argv*/)
TEST_HAS_FACETS(std::num_put, l);
TEST_HAS_FACETS(std::time_put, l);
TEST_HAS_FACETS(std::numpunct, l);
TEST_HAS_FACETS(bl::numpunct, l);
TEST_HAS_FACETS(std::moneypunct, l);
// Parsing
TEST_HAS_FACETS(std::num_get, l);
Expand Down
Loading
Loading