// Copyright 2017 Elias Kosunen // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This file is a part of scnlib: // https://github.com/eliaskosunen/scnlib #if defined(SCN_HEADER_ONLY) && SCN_HEADER_ONLY #define SCN_LOCALE_CPP #endif #include #include #include #include #include #include #include #include namespace scn { SCN_BEGIN_NAMESPACE namespace detail { template struct locale_data { using char_type = CharT; using string_type = std::basic_string; std::locale global_locale{std::locale()}; std::locale classic_locale{std::locale::classic()}; string_type truename{}; string_type falsename{}; char_type decimal_point{}; char_type thousands_separator{}; }; template const std::locale& to_locale(const basic_custom_locale_ref& l) { SCN_EXPECT(l.get_locale()); return *static_cast(l.get_locale()); } // Buggy on gcc 5 and 6 SCN_GCC_PUSH SCN_GCC_IGNORE("-Wmaybe-uninitialized") template basic_custom_locale_ref::basic_custom_locale_ref() { auto data = new locale_data{}; m_data = data; m_locale = &data->global_locale; _initialize(); } template basic_custom_locale_ref::basic_custom_locale_ref( const void* locale) : m_locale(locale) { auto data = new locale_data{}; m_data = data; if (!locale) { m_locale = &data->global_locale; } _initialize(); } SCN_GCC_POP template void basic_custom_locale_ref::_initialize() { const auto& facet = std::use_facet>(to_locale(*this)); auto& data = *static_cast*>(m_data); data.truename = facet.truename(); data.falsename = facet.falsename(); data.decimal_point = facet.decimal_point(); data.thousands_separator = facet.thousands_sep(); } template basic_custom_locale_ref::basic_custom_locale_ref( basic_custom_locale_ref&& o) { m_data = o.m_data; m_locale = o.m_locale; o.m_data = nullptr; o.m_locale = nullptr; _initialize(); } template auto basic_custom_locale_ref::operator=( basic_custom_locale_ref&& o) -> basic_custom_locale_ref& { delete static_cast*>(m_data); m_data = o.m_data; m_locale = o.m_locale; o.m_data = nullptr; o.m_locale = nullptr; _initialize(); return *this; } template basic_custom_locale_ref::~basic_custom_locale_ref() { delete static_cast*>(m_data); } template auto basic_custom_locale_ref::make_classic() -> basic_custom_locale_ref { basic_custom_locale_ref loc{}; loc.convert_to_classic(); return loc; } template void basic_custom_locale_ref::convert_to_classic() { m_locale = &static_cast*>(m_data)->classic_locale; } template void basic_custom_locale_ref::convert_to_global() { SCN_EXPECT(m_data); m_locale = &static_cast*>(m_data)->global_locale; } template bool basic_custom_locale_ref::do_is_space(char_type ch) const { return std::isspace(ch, to_locale(*this)); } template bool basic_custom_locale_ref::do_is_digit(char_type ch) const { return std::isdigit(ch, to_locale(*this)); } template auto basic_custom_locale_ref::do_decimal_point() const -> char_type { return static_cast*>(m_data)->decimal_point; } template auto basic_custom_locale_ref::do_thousands_separator() const -> char_type { return static_cast*>(m_data) ->thousands_separator; } template auto basic_custom_locale_ref::do_truename() const -> string_view_type { const auto& str = static_cast*>(m_data)->truename; return {str.data(), str.size()}; } template auto basic_custom_locale_ref::do_falsename() const -> string_view_type { const auto& str = static_cast*>(m_data)->falsename; return {str.data(), str.size()}; } static inline error convert_to_wide_impl(const std::locale&, const char*, const char*, const char*&, wchar_t*, wchar_t*, wchar_t*&) { SCN_EXPECT(false); SCN_UNREACHABLE; } static inline error convert_to_wide_impl(const std::locale&, const wchar_t*, const wchar_t*, const wchar_t*&, wchar_t*, wchar_t*, wchar_t*&) { SCN_EXPECT(false); SCN_UNREACHABLE; } template error basic_custom_locale_ref::convert_to_wide( const CharT* from_begin, const CharT* from_end, const CharT*& from_next, wchar_t* to_begin, wchar_t* to_end, wchar_t*& to_next) const { return convert_to_wide_impl(to_locale(*this), from_begin, from_end, from_next, to_begin, to_end, to_next); } static inline expected convert_to_wide_impl(const std::locale&, const char*, const char*) { SCN_EXPECT(false); SCN_UNREACHABLE; } static inline expected convert_to_wide_impl(const std::locale&, const wchar_t*, const wchar_t*) { SCN_EXPECT(false); SCN_UNREACHABLE; } template expected basic_custom_locale_ref::convert_to_wide( const CharT* from_begin, const CharT* from_end) const { return convert_to_wide_impl(to_locale(*this), from_begin, from_end); } template bool basic_custom_locale_ref::do_is_space( span ch) const { const auto& locale = to_locale(*this); if (sizeof(CharT) == 1) { SCN_EXPECT(ch.size() >= 1); code_point cp{}; auto it = parse_code_point(ch.begin(), ch.end(), cp); SCN_EXPECT(it); return is_space(cp); } SCN_EXPECT(ch.size() == 1); return std::isspace(ch[0], locale); } template bool basic_custom_locale_ref::do_is_digit( span ch) const { const auto& locale = to_locale(*this); if (sizeof(CharT) == 1) { SCN_EXPECT(ch.size() >= 1); code_point cp{}; auto it = parse_code_point(ch.begin(), ch.end(), cp); SCN_EXPECT(it); return is_digit(cp); } SCN_EXPECT(ch.size() == 1); return std::isdigit(ch[0], locale); } #define SCN_DEFINE_CUSTOM_LOCALE_CTYPE(f) \ template \ bool basic_custom_locale_ref::is_##f(char_type ch) const \ { \ return std::is##f(ch, to_locale(*this)); \ } \ template \ bool basic_custom_locale_ref::is_##f(code_point cp) const \ { \ return std::is##f(static_cast(cp), to_locale(*this)); \ } \ template \ bool basic_custom_locale_ref::is_##f(span ch) \ const \ { \ const auto& locale = to_locale(*this); \ if (sizeof(CharT) == 1) { \ SCN_EXPECT(ch.size() >= 1); \ code_point cp{}; \ auto it = parse_code_point(ch.begin(), ch.end(), cp); \ SCN_EXPECT(it); \ return is_##f(cp); \ } \ SCN_EXPECT(ch.size() == 1); \ return std::is##f(ch[0], locale); \ } SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alnum) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(alpha) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(cntrl) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(graph) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(lower) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(print) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(punct) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(upper) SCN_DEFINE_CUSTOM_LOCALE_CTYPE(xdigit) #undef SCN_DEFINE_CUSTOM_LOCALE_CTYPE template bool basic_custom_locale_ref::is_space(code_point cp) const { return std::isspace(static_cast(cp), to_locale(*this)); } template bool basic_custom_locale_ref::is_digit(code_point cp) const { return std::isdigit(static_cast(cp), to_locale(*this)); } // For some reason, there's no isblank in libc++ template bool basic_custom_locale_ref::is_blank(char_type ch) const { return std::use_facet>(to_locale(*this)) .is(std::ctype_base::blank, ch); } template bool basic_custom_locale_ref::is_blank(code_point ch) const { return std::use_facet>(to_locale(*this)) .is(std::ctype_base::blank, static_cast(ch)); } template bool basic_custom_locale_ref::is_blank( span ch) const { const auto& locale = to_locale(*this); if (sizeof(CharT) == 1) { SCN_EXPECT(ch.size() >= 1); code_point cp{}; auto it = parse_code_point(ch.begin(), ch.end(), cp); SCN_EXPECT(it); return is_blank(cp); } SCN_EXPECT(ch.size() == 1); return std::use_facet>(locale).is( std::ctype_base::blank, ch[0]); } template auto read_num_check_range(T val) -> typename std::enable_if::value, error>::type { if (val == std::numeric_limits::max()) { return error(error::value_out_of_range, "Scanned number out of range: overflow"); } if (val == std::numeric_limits::min()) { return error(error::value_out_of_range, "Scanned number out of range: underflow"); } return error(error::invalid_scanned_value, "Localized number read failed"); } template auto read_num_check_range(T val) -> typename std::enable_if::value, error>::type { SCN_GCC_COMPAT_PUSH SCN_GCC_COMPAT_IGNORE("-Wfloat-equal") if (val == std::numeric_limits::max() || val == -std::numeric_limits::max()) { return error(error::value_out_of_range, "Scanned number out of range: overflow"); } if (val == zero_value::value) { return error(error::value_out_of_range, "Scanned number out of range: underflow"); } SCN_GCC_COMPAT_POP return error(error::invalid_scanned_value, "Localized number read failed"); } template error do_read_num_impl(T& val, std::basic_istringstream& ss) { ss >> val; return {}; } template error do_read_num_impl(CharT& val, std::basic_istringstream& ss) { long long tmp; if (!(ss >> tmp)) { return {}; } if (tmp > std::numeric_limits::max()) { return {error::value_out_of_range, "Scanned number out of range: overflow"}; } if (tmp < std::numeric_limits::min()) { return {error::value_out_of_range, "Scanned number out of range: underflow"}; } val = static_cast(tmp); return {}; } template error do_read_num_impl(signed char& val, std::basic_istringstream& ss) { int tmp; if (!(ss >> tmp)) { return {}; } if (tmp > std::numeric_limits::max()) { return {error::value_out_of_range, "Scanned number out of range: overflow"}; } if (tmp < std::numeric_limits::min()) { return {error::value_out_of_range, "Scanned number out of range: underflow"}; } val = static_cast(tmp); return {}; } template error do_read_num_impl(unsigned char& val, std::basic_istringstream& ss) { int tmp; if (!(ss >> tmp)) { return {}; } if (tmp > std::numeric_limits::max()) { return {error::value_out_of_range, "Scanned number out of range: overflow"}; } if (tmp < 0) { return {error::value_out_of_range, "Scanned number out of range: underflow"}; } val = static_cast(tmp); return {}; } template expected do_read_num( T& val, const std::locale& loc, const std::basic_string& buf, int base) { #if SCN_HAS_EXCEPTIONS std::basic_istringstream ss(buf); ss.imbue(loc); ss >> std::setbase(base); try { T tmp; auto e = do_read_num_impl(tmp, ss); if (ss.bad()) { return error(error::unrecoverable_internal_error, "Localized stringstream is bad"); } if (!e) { return e; } if (ss.fail()) { return read_num_check_range(tmp); } val = tmp; } catch (const std::ios_base::failure& f) { return error(error::invalid_scanned_value, f.what()); } return ss.eof() ? static_cast(buf.size()) : static_cast(ss.tellg()); #else SCN_UNUSED(val); SCN_UNUSED(loc); SCN_UNUSED(buf); return error(error::exceptions_required, "Localized number reading is only supported with " "exceptions enabled"); #endif } template <> expected do_read_num(wchar_t&, const std::locale&, const std::string&, int) { SCN_EXPECT(false); SCN_UNREACHABLE; } template <> expected do_read_num(char&, const std::locale&, const std::wstring&, int) { SCN_EXPECT(false); SCN_UNREACHABLE; } template template expected basic_custom_locale_ref::read_num( T& val, const string_type& buf, int b) const { return do_read_num(val, to_locale(*this), buf, b); } #if SCN_INCLUDE_SOURCE_DEFINITIONS SCN_CLANG_PUSH SCN_CLANG_IGNORE("-Wpadded") SCN_CLANG_IGNORE("-Wweak-template-vtables") template class basic_custom_locale_ref; template class basic_custom_locale_ref; SCN_CLANG_POP template expected basic_custom_locale_ref::read_num(signed char&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(short&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(int&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(long&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(long long&, const string_type&, int) const; template expected basic_custom_locale_ref< char>::read_num(unsigned char&, const string_type&, int) const; template expected basic_custom_locale_ref< char>::read_num(unsigned short&, const string_type&, int) const; template expected basic_custom_locale_ref< char>::read_num(unsigned int&, const string_type&, int) const; template expected basic_custom_locale_ref< char>::read_num(unsigned long&, const string_type&, int) const; template expected basic_custom_locale_ref< char>::read_num(unsigned long long&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(char&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(wchar_t&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(float&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(double&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(long double&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(signed char&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(short&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(int&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(long&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(long long&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(unsigned char&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(unsigned short&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(unsigned int&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(unsigned long&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(unsigned long long&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(float&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(double&, const string_type&, int) const; template expected basic_custom_locale_ref< wchar_t>::read_num(long double&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(char&, const string_type&, int) const; template expected basic_custom_locale_ref::read_num(wchar_t&, const string_type&, int) const; #endif } // namespace detail SCN_END_NAMESPACE } // namespace scn