|
| 1 | +// |
| 2 | +// Copyright (c) 2023 Alexander Grund |
| 3 | +// |
| 4 | +// Distributed under the Boost Software License, Version 1.0. |
| 5 | +// https://www.boost.org/LICENSE_1_0.txt |
| 6 | + |
| 7 | +#include <boost/locale.hpp> |
| 8 | +#include <boost/locale/util/string.hpp> |
| 9 | +#include "boostLocale/test/tools.hpp" |
| 10 | +#include "boostLocale/test/unit_test.hpp" |
| 11 | +#include <limits> |
| 12 | +#include <map> |
| 13 | +#include <sstream> |
| 14 | +#include <string> |
| 15 | + |
| 16 | +namespace bl = boost::locale; |
| 17 | + |
| 18 | +template<typename Char> |
| 19 | +void as_if_std_numpunct(const std::locale& l) |
| 20 | +{ |
| 21 | + const std::numpunct<Char>& std_facet = std::use_facet<std::numpunct<Char>>(l); |
| 22 | + const bl::numpunct<Char>& boost_facet = std::use_facet<bl::numpunct<Char>>(l); |
| 23 | + // All functions present in std::numpunct are also present in boost::locale::numpunct and yield the same results |
| 24 | + TEST_REQUIRE(dynamic_cast<const bl::numpunct<Char>*>(&std_facet)); // In fact they are equal |
| 25 | + TEST_EQ(std_facet.decimal_point(), boost_facet.decimal_point()); |
| 26 | + TEST_EQ(std_facet.thousands_sep(), boost_facet.thousands_sep()); |
| 27 | + TEST_EQ(std_facet.grouping(), boost_facet.grouping()); |
| 28 | + TEST_EQ(std_facet.truename(), boost_facet.truename()); |
| 29 | + TEST_EQ(std_facet.falsename(), boost_facet.falsename()); |
| 30 | +} |
| 31 | + |
| 32 | +namespace { |
| 33 | +template<typename Char = char> |
| 34 | +struct Punctuation { |
| 35 | + std::basic_string<Char> decimal; |
| 36 | + std::basic_string<Char> thousand; |
| 37 | +}; |
| 38 | + |
| 39 | +const std::map<std::string, Punctuation<>> expected_punctuations = { |
| 40 | + {"en", {".", ","}}, |
| 41 | + {"de", {",", "."}}, |
| 42 | + {"he", {".", ","}}, |
| 43 | + {"ja", {".", ","}}, |
| 44 | + {"ru", {",", "\xC2\xA0"}}, |
| 45 | + {"it", {".", ","}}, |
| 46 | +}; |
| 47 | + |
| 48 | +template<typename Char, template<typename> class Res> |
| 49 | +Res<Char> get_expected(const std::map<std::string, Res<char>>& from, const std::locale& l) |
| 50 | +{ |
| 51 | + const auto& src = from.at(std::use_facet<bl::info>(l).language()); |
| 52 | + return {to_correct_string<Char>(src.decimal, l), to_correct_string<Char>(src.thousand, l)}; |
| 53 | +} |
| 54 | + |
| 55 | +template<typename Char> |
| 56 | +struct split_result { |
| 57 | + std::basic_string<Char> digits, others; |
| 58 | +}; |
| 59 | + |
| 60 | +template<typename Char> |
| 61 | +split_result<Char> split_number(const std::basic_string<Char>& s) |
| 62 | +{ |
| 63 | + split_result<Char> res; |
| 64 | + for(Char c : s) { |
| 65 | + if(c >= std::numeric_limits<char>::min() && c <= std::numeric_limits<char>::max() |
| 66 | + && boost::locale::util::is_numeric_ascii(static_cast<char>(c))) |
| 67 | + res.digits += c; |
| 68 | + else |
| 69 | + res.others += c; |
| 70 | + } |
| 71 | + return res; |
| 72 | +} |
| 73 | +} // namespace |
| 74 | + |
| 75 | +template<typename Char> |
| 76 | +void test_for_char(const std::locale& l) |
| 77 | +{ |
| 78 | + using string_type = std::basic_string<Char>; |
| 79 | + as_if_std_numpunct<Char>(l); |
| 80 | + { |
| 81 | + const auto& expected = get_expected<Char>(expected_punctuations, l); |
| 82 | + const auto& boost_facet = std::use_facet<bl::numpunct<Char>>(l); |
| 83 | + TEST_EQ(boost_facet.decimal_point_str(), expected.decimal); |
| 84 | + TEST_EQ(boost_facet.thousands_sep_str(), expected.thousand); |
| 85 | + } |
| 86 | + std::basic_ostringstream<Char> s; |
| 87 | + s.imbue(l); |
| 88 | + // Formatting not using the Boost.Locale modifiers uses (only) the std::numpunct values |
| 89 | + { |
| 90 | + const auto& facet = std::use_facet<std::numpunct<Char>>(l); |
| 91 | + empty_stream(s) << 1234567890; |
| 92 | + auto actual = split_number(s.str()); |
| 93 | + TEST_EQ(actual.digits, ascii_to<Char>("1234567890")); |
| 94 | + TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep())); |
| 95 | + |
| 96 | + empty_stream(s) << 12.25; |
| 97 | + actual = split_number(s.str()); |
| 98 | + TEST_EQ(actual.digits, ascii_to<Char>("1225")); |
| 99 | + TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point())); |
| 100 | + } |
| 101 | + // Formatting using the Boost.Locale modifiers uses the boost::locale::numpunct values |
| 102 | + s << bl::as::number; |
| 103 | + { |
| 104 | + const auto& facet = std::use_facet<bl::numpunct<Char>>(l); |
| 105 | + empty_stream(s) << 1234567890; |
| 106 | + auto actual = split_number(s.str()); |
| 107 | + TEST_EQ(actual.digits, ascii_to<Char>("1234567890")); |
| 108 | + TEST_EQ(actual.others, string_type(actual.others.size(), facet.thousands_sep())); |
| 109 | + |
| 110 | + empty_stream(s) << 12.25; |
| 111 | + actual = split_number(s.str()); |
| 112 | + TEST_EQ(actual.digits, ascii_to<Char>("1225")); |
| 113 | + TEST_EQ(actual.others, string_type(actual.others.size(), facet.decimal_point())); |
| 114 | + } |
| 115 | +} |
| 116 | + |
| 117 | +void test_for_locale(const std::string& name) |
| 118 | +{ |
| 119 | + std::cout << "-- Locale: " << name << '\n'; |
| 120 | + const std::locale l = bl::generator{}(name); |
| 121 | + std::cout << "---- char\n"; |
| 122 | + test_for_char<char>(l); |
| 123 | + std::cout << "---- wchar_t\n"; |
| 124 | + test_for_char<wchar_t>(l); |
| 125 | +#ifdef BOOST_LOCALE_ENABLE_CHAR16_T |
| 126 | + std::cout << "---- char16_t\n"; |
| 127 | + test_for_char<char16_t>(l); |
| 128 | +#endif |
| 129 | +#ifdef BOOST_LOCALE_ENABLE_CHAR32_T |
| 130 | + std::cout << "---- char32_t\n"; |
| 131 | + test_for_char<char32_t>(l); |
| 132 | +#endif |
| 133 | +} |
| 134 | + |
| 135 | +void test_main(int /*argc*/, char** /*argv*/) |
| 136 | +{ |
| 137 | + const bl::localization_backend_manager orig_backend = bl::localization_backend_manager::global(); |
| 138 | + for(const std::string& backendName : orig_backend.get_all_backends()) { |
| 139 | + std::cout << "Backend: " << backendName << std::endl; |
| 140 | + bl::localization_backend_manager tmp_backend = bl::localization_backend_manager::global(); |
| 141 | + tmp_backend.select(backendName); |
| 142 | + bl::localization_backend_manager::global(tmp_backend); |
| 143 | + test_for_locale("en_US.UTF-8"); |
| 144 | + test_for_locale("de_DE.UTF-8"); |
| 145 | + test_for_locale("he_IL.UTF-8"); |
| 146 | + test_for_locale("ja_JP.UTF-8"); |
| 147 | + test_for_locale("ru_RU.UTF-8"); |
| 148 | + test_for_locale("it_IT"); |
| 149 | + } |
| 150 | +} |
0 commit comments