diff --git a/CMakeLists.txt b/CMakeLists.txt index 32f10c9c..c0b4e693 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ # Copyright 2020, 2021 Peter Dimov -# Copyright 2022-2024 Alexander Grund +# Copyright 2022-2025 Alexander Grund # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt @@ -35,6 +35,7 @@ add_library(boost_locale src/util/locale_data.cpp src/util/make_std_unique.hpp src/util/numeric.hpp + src/util/numeric_conversion.hpp src/util/timezone.hpp ${headers} ) @@ -50,8 +51,8 @@ target_link_libraries(boost_locale Boost::config Boost::core Boost::iterator - Boost::utility PRIVATE + Boost::charconv Boost::predef Boost::thread ) diff --git a/build.jam b/build.jam index 573ca20a..a9b6ec74 100644 --- a/build.jam +++ b/build.jam @@ -1,4 +1,5 @@ # Copyright René Ferdinand Rivera Morell 2023-2024 +# Copyright(c) 2025 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) @@ -20,7 +21,7 @@ constant boost_dependencies : /boost/config//boost_config /boost/core//boost_core /boost/iterator//boost_iterator - /boost/utility//boost_utility ; + ; project /boost/locale : common-requirements diff --git a/build/Jamfile.v2 b/build/Jamfile.v2 index 7a07e787..1f77dc19 100644 --- a/build/Jamfile.v2 +++ b/build/Jamfile.v2 @@ -16,6 +16,7 @@ import toolset ; path-constant TOP : .. ; constant boost_dependencies_private : + /boost/charconv//boost_charconv /boost/predef//boost_predef /boost/thread//boost_thread ; diff --git a/include/boost/locale/boundary/boundary_point.hpp b/include/boost/locale/boundary/boundary_point.hpp index 370dd5f8..0743347c 100644 --- a/include/boost/locale/boundary/boundary_point.hpp +++ b/include/boost/locale/boundary/boundary_point.hpp @@ -100,7 +100,7 @@ namespace boost { namespace locale { namespace boundary { typedef boundary_point sboundary_point; ///< convenience typedef typedef boundary_point wsboundary_point; ///< convenience typedef -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t typedef boundary_point u8sboundary_point; ///< convenience typedef #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T diff --git a/include/boost/locale/boundary/index.hpp b/include/boost/locale/boundary/index.hpp index 92b7613f..249ef877 100644 --- a/include/boost/locale/boundary/index.hpp +++ b/include/boost/locale/boundary/index.hpp @@ -868,7 +868,7 @@ namespace boost { namespace locale { namespace boundary { typedef segment_index ssegment_index; ///< convenience typedef typedef segment_index wssegment_index; ///< convenience typedef -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t typedef segment_index u8ssegment_index; ///< convenience typedef #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T @@ -892,7 +892,7 @@ namespace boost { namespace locale { namespace boundary { typedef boundary_point_index sboundary_point_index; ///< convenience typedef typedef boundary_point_index wsboundary_point_index; ///< convenience typedef -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t typedef boundary_point_index u8sboundary_point_index; ///< convenience typedef #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T diff --git a/include/boost/locale/boundary/segment.hpp b/include/boost/locale/boundary/segment.hpp index 24e129dc..84243abe 100644 --- a/include/boost/locale/boundary/segment.hpp +++ b/include/boost/locale/boundary/segment.hpp @@ -340,7 +340,7 @@ namespace boost { namespace locale { namespace boundary { typedef segment ssegment; ///< convenience typedef typedef segment wssegment; ///< convenience typedef -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t typedef segment u8ssegment; ///< convenience typedef #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T diff --git a/include/boost/locale/config.hpp b/include/boost/locale/config.hpp index 1c69c92d..2064810d 100644 --- a/include/boost/locale/config.hpp +++ b/include/boost/locale/config.hpp @@ -89,11 +89,6 @@ # define BOOST_LOCALE_NO_SANITIZE(what) #endif -#if !defined(__cpp_lib_char8_t) || BOOST_WORKAROUND(BOOST_CLANG_VERSION, < 150000) -// No std::basic_string or bug in Clang: https://github.com/llvm/llvm-project/issues/55560 -# define BOOST_LOCALE_NO_CXX20_STRING8 -#endif - /// \endcond #endif // boost/locale/config.hpp diff --git a/include/boost/locale/detail/any_string.hpp b/include/boost/locale/detail/any_string.hpp index c0cc7ffb..473d07e7 100644 --- a/include/boost/locale/detail/any_string.hpp +++ b/include/boost/locale/detail/any_string.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2023 Alexander Grund +// Copyright (c) 2023-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include #include @@ -31,7 +31,7 @@ namespace boost { namespace locale { namespace detail { }; template struct BOOST_SYMBOL_VISIBLE impl : base { - explicit impl(const boost::basic_string_view value) : s(value) {} + explicit impl(const core::basic_string_view value) : s(value) {} impl* clone() const override { return new impl(*this); } std::basic_string s; }; @@ -49,7 +49,7 @@ namespace boost { namespace locale { namespace detail { } template - void set(const boost::basic_string_view s) + void set(const core::basic_string_view s) { BOOST_ASSERT(!s.empty()); s_.reset(new impl(s)); diff --git a/include/boost/locale/detail/encoding.hpp b/include/boost/locale/detail/encoding.hpp index ee669349..2e37c308 100644 --- a/include/boost/locale/detail/encoding.hpp +++ b/include/boost/locale/detail/encoding.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022-2023 Alexander Grund +// Copyright (c) 2022-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -9,7 +9,7 @@ #include #include -#include +#include #include #include @@ -24,7 +24,7 @@ namespace boost { namespace locale { namespace conv { namespace detail { virtual ~charset_converter() = default; virtual string_type convert(const CharIn* begin, const CharIn* end) = 0; - string_type convert(const boost::basic_string_view& text) + string_type convert(const core::basic_string_view text) { return convert(text.data(), text.data() + text.length()); } diff --git a/include/boost/locale/encoding.hpp b/include/boost/locale/encoding.hpp index d0e1d9d0..abc68bac 100644 --- a/include/boost/locale/encoding.hpp +++ b/include/boost/locale/encoding.hpp @@ -1,5 +1,6 @@ // // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -240,11 +241,11 @@ namespace boost { namespace locale { /// Convert \a text to UTF /// /// \throws conversion_error: Conversion failed - string_type convert(const boost::string_view& text) const { return impl_->convert(text); } + string_type convert(const core::string_view text) const { return impl_->convert(text); } /// Convert \a text to UTF /// /// \throws conversion_error: Conversion failed - string_type operator()(const boost::string_view& text) const { return convert(text); } + string_type operator()(const core::string_view text) const { return convert(text); } }; /// Converter class to decode an UTF string and encode it using a local encoding @@ -254,7 +255,7 @@ namespace boost { namespace locale { public: using char_type = CharType; - using stringview_type = boost::basic_string_view; + using stringview_type = core::basic_string_view; /// Create an instance to convert UTF text to text encoded with \a charset according to policy \a how /// @@ -298,11 +299,11 @@ namespace boost { namespace locale { /// Convert \a text /// /// \throws conversion_error: Conversion failed - std::string convert(const boost::string_view& text) const { return impl_->convert(text); } + std::string convert(const core::string_view text) const { return impl_->convert(text); } /// Convert \a text /// /// \throws conversion_error: Conversion failed - std::string operator()(const boost::string_view& text) const { return convert(text); } + std::string operator()(const core::string_view text) const { return convert(text); } }; } // namespace conv }} // namespace boost::locale diff --git a/include/boost/locale/format.hpp b/include/boost/locale/format.hpp index 6adb8440..68020a4c 100644 --- a/include/boost/locale/format.hpp +++ b/include/boost/locale/format.hpp @@ -1,6 +1,6 @@ // // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) -// Copyright (c) 2021-2023 Alexander Grund +// Copyright (c) 2021-2024 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -418,7 +418,7 @@ namespace boost { namespace locale { typedef basic_format format; /// Definition of wchar_t based format typedef basic_format wformat; -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t /// Definition of char8_t based format typedef basic_format u8format; #endif diff --git a/include/boost/locale/formatting.hpp b/include/boost/locale/formatting.hpp index e3c8619e..1844398a 100644 --- a/include/boost/locale/formatting.hpp +++ b/include/boost/locale/formatting.hpp @@ -122,7 +122,7 @@ namespace boost { namespace locale { /// Set time zone for formatting dates and time void time_zone(const std::string&); /// Get time zone for formatting dates and time - std::string time_zone() const; + const std::string& time_zone() const; /// Set date/time pattern (strftime like) template diff --git a/include/boost/locale/message.hpp b/include/boost/locale/message.hpp index 88a4aefb..fafff385 100644 --- a/include/boost/locale/message.hpp +++ b/include/boost/locale/message.hpp @@ -341,7 +341,7 @@ namespace boost { namespace locale { typedef basic_message message; /// Convenience typedef for wchar_t typedef basic_message wmessage; -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t /// Convenience typedef for char8_t typedef basic_message u8message; #endif diff --git a/src/encoding/codepage.cpp b/src/encoding/codepage.cpp index 0f052b82..814a8035 100644 --- a/src/encoding/codepage.cpp +++ b/src/encoding/codepage.cpp @@ -224,7 +224,7 @@ namespace boost { namespace locale { namespace conv { BOOST_LOCALE_INSTANTIATE(char); BOOST_LOCALE_INSTANTIATE_NO_CHAR(wchar_t); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t BOOST_LOCALE_INSTANTIATE_NO_CHAR(char8_t); #endif diff --git a/src/icu/conversion.cpp b/src/icu/conversion.cpp index 130a0d4e..a7fddb47 100644 --- a/src/icu/conversion.cpp +++ b/src/icu/conversion.cpp @@ -198,7 +198,7 @@ namespace boost { namespace locale { namespace impl_icu { return std::locale(in, new utf8_converter_impl(cd)); return std::locale(in, new converter_impl(cd)); case char_facet_t::wchar_f: return std::locale(in, new converter_impl(cd)); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t case char_facet_t::char8_f: return std::locale(in, new utf8_converter_impl(cd)); #elif defined(__cpp_char8_t) case char_facet_t::char8_f: break; diff --git a/src/posix/converter.cpp b/src/posix/converter.cpp index 38a4bb69..13477aa0 100644 --- a/src/posix/converter.cpp +++ b/src/posix/converter.cpp @@ -125,7 +125,7 @@ namespace boost { namespace locale { namespace impl_posix { return std::locale(in, new std_converter(std::move(lc))); } case char_facet_t::wchar_f: return std::locale(in, new std_converter(std::move(lc))); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t case char_facet_t::char8_f: return std::locale(in, new utf8_converter(std::move(lc))); #elif defined(__cpp_char8_t) case char_facet_t::char8_f: break; diff --git a/src/shared/format.cpp b/src/shared/format.cpp index ed2fb2ea..9853c7ff 100644 --- a/src/shared/format.cpp +++ b/src/shared/format.cpp @@ -1,5 +1,6 @@ // // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) +// Copyright (c) 2024 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -7,7 +8,7 @@ #include #include #include -#include "../util/numeric.hpp" +#include "../util/numeric_conversion.hpp" #include #include #include @@ -63,10 +64,9 @@ namespace boost { namespace locale { namespace detail { { if(key.empty()) return; - int position; - if(util::try_to_int(key, position) && position > 0) { - static_assert(sizeof(unsigned) <= sizeof(decltype(d->position)), "Possible lossy conversion"); - d->position = static_cast(position - 1); + decltype(d->position) position; + if(util::try_to_int(key, position) && position > 0u) { + d->position = position - 1u; } else if(key == "num" || key == "number") { as::number(ios_); diff --git a/src/shared/formatting.cpp b/src/shared/formatting.cpp index 1dc9b3a9..53f436a3 100644 --- a/src/shared/formatting.cpp +++ b/src/shared/formatting.cpp @@ -65,7 +65,7 @@ namespace boost { namespace locale { { time_zone_ = tz; } - std::string ios_info::time_zone() const + const std::string& ios_info::time_zone() const { return time_zone_; } diff --git a/src/shared/message.cpp b/src/shared/message.cpp index 27aaa671..70eafd22 100644 --- a/src/shared/message.cpp +++ b/src/shared/message.cpp @@ -1,6 +1,6 @@ // // Copyright (c) 2009-2015 Artyom Beilis (Tonkikh) -// Copyright (c) 2021-2023 Alexander Grund +// Copyright (c) 2021-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -26,7 +26,7 @@ #include "mo_hash.hpp" #include "mo_lambda.hpp" #include -#include +#include #include #include #include @@ -141,7 +141,7 @@ namespace boost { namespace locale { namespace gnu_gettext { hash_offset_ = get(24); } - string_view find(const char* context_in, const char* key_in) const + core::string_view find(const char* context_in, const char* key_in) const { if(!has_hash()) return {}; @@ -192,13 +192,13 @@ namespace boost { namespace locale { namespace gnu_gettext { return data_.data() + off; } - string_view value(unsigned id) const + core::string_view value(unsigned id) const { const uint32_t len = get(translations_offset_ + id * 8); const uint32_t off = get(translations_offset_ + id * 8 + 4); if(len > data_.size() || off > data_.size() - len) throw std::runtime_error("Bad mo-file format"); - return string_view(&data_[off], len); + return core::string_view(&data_[off], len); } bool has_hash() const { return hash_size_ != 0; } @@ -233,7 +233,7 @@ namespace boost { namespace locale { namespace gnu_gettext { template struct mo_file_use_traits { static constexpr bool in_use = false; - using string_view_type = basic_string_view; + using string_view_type = core::basic_string_view; static string_view_type use(const mo_file&, const CharType*, const CharType*) { throw std::logic_error("Unexpected call"); // LCOV_EXCL_LINE @@ -243,7 +243,7 @@ namespace boost { namespace locale { namespace gnu_gettext { template<> struct mo_file_use_traits { static constexpr bool in_use = true; - using string_view_type = basic_string_view; + using string_view_type = core::basic_string_view; static string_view_type use(const mo_file& mo, const char* context, const char* key) { return mo.find(context, key); @@ -254,10 +254,10 @@ namespace boost { namespace locale { namespace gnu_gettext { template<> struct mo_file_use_traits { static constexpr bool in_use = true; - using string_view_type = basic_string_view; + using string_view_type = core::basic_string_view; static string_view_type use(const mo_file& mo, const char8_t* context, const char8_t* key) { - string_view res = mo.find(reinterpret_cast(context), reinterpret_cast(key)); + core::string_view res = mo.find(reinterpret_cast(context), reinterpret_cast(key)); return {reinterpret_cast(res.data()), res.size()}; } }; @@ -551,10 +551,10 @@ namespace boost { namespace locale { namespace gnu_gettext { return true; } - static std::string extract(boost::string_view meta, const std::string& key, const boost::string_view separators) + static std::string extract(core::string_view meta, const std::string& key, const core::string_view separators) { const size_t pos = meta.find(key); - if(pos == boost::string_view::npos) + if(pos == core::string_view::npos) return ""; meta.remove_prefix(pos + key.size()); const size_t end_pos = meta.find_first_of(separators); @@ -620,7 +620,7 @@ namespace boost { namespace locale { namespace detail { case char_facet_t::nochar: break; case char_facet_t::char_f: return std::locale(in, gnu_gettext::create_messages_facet(minf)); case char_facet_t::wchar_f: return std::locale(in, gnu_gettext::create_messages_facet(minf)); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t case char_facet_t::char8_f: return std::locale(in, gnu_gettext::create_messages_facet(minf)); #elif defined(__cpp_char8_t) case char_facet_t::char8_f: break; diff --git a/src/std/converter.cpp b/src/std/converter.cpp index fcfb7df7..ec60a58d 100644 --- a/src/std/converter.cpp +++ b/src/std/converter.cpp @@ -104,7 +104,7 @@ namespace boost { namespace locale { namespace impl_std { else return std::locale(in, new std_converter(locale_name)); case char_facet_t::wchar_f: return std::locale(in, new std_converter(locale_name)); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t case char_facet_t::char8_f: return std::locale(in, new utf8_converter(locale_name)); #elif defined(__cpp_char8_t) case char_facet_t::char8_f: break; diff --git a/src/std/std_backend.cpp b/src/std/std_backend.cpp index 0bcd2800..1eadfd24 100644 --- a/src/std/std_backend.cpp +++ b/src/std/std_backend.cpp @@ -27,7 +27,7 @@ #include "../util/encoding.hpp" #include "../util/gregorian.hpp" #include "../util/make_std_unique.hpp" -#include "../util/numeric.hpp" +#include "../util/numeric_conversion.hpp" #include "all_generator.hpp" namespace { diff --git a/src/util/encoding.cpp b/src/util/encoding.cpp index 18b658fa..8b893f1c 100644 --- a/src/util/encoding.cpp +++ b/src/util/encoding.cpp @@ -1,6 +1,6 @@ // // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) -// Copyright (c) 2022-2023 Alexander Grund +// Copyright (c) 2022-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -18,7 +18,7 @@ #include namespace boost { namespace locale { namespace util { - std::string normalize_encoding(const string_view encoding) + std::string normalize_encoding(const core::string_view encoding) { std::string result; result.reserve(encoding.length()); @@ -51,7 +51,7 @@ namespace boost { namespace locale { namespace util { return -1; } - int encoding_to_windows_codepage(const string_view encoding) + int encoding_to_windows_codepage(const core::string_view encoding) { return normalized_encoding_to_windows_codepage(normalize_encoding(encoding)); } diff --git a/src/util/encoding.hpp b/src/util/encoding.hpp index 0f4c3b56..2f73d49a 100644 --- a/src/util/encoding.hpp +++ b/src/util/encoding.hpp @@ -1,6 +1,6 @@ // // Copyright (c) 2009-2011 Artyom Beilis (Tonkikh) -// Copyright (c) 2022-2023 Alexander Grund +// Copyright (c) 2022-2025 Alexander Grund // // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt @@ -9,7 +9,7 @@ #define BOOST_LOCALE_UTIL_ENCODING_HPP #include -#include +#include #include #include #include @@ -48,7 +48,7 @@ namespace boost { namespace locale { namespace util { #endif /// Make encoding lowercase and remove all non-alphanumeric characters - BOOST_LOCALE_DECL std::string normalize_encoding(string_view encoding); + BOOST_LOCALE_DECL std::string normalize_encoding(core::string_view encoding); /// True if the normalized encodings are equal inline bool are_encodings_equal(const std::string& l, const std::string& r) { @@ -58,10 +58,10 @@ namespace boost { namespace locale { namespace util { BOOST_LOCALE_DECL std::vector get_simple_encodings(); #if BOOST_LOCALE_USE_WIN32_API - int encoding_to_windows_codepage(string_view encoding); + int encoding_to_windows_codepage(core::string_view encoding); #else // Requires WinAPI -> Dummy returning invalid - inline int encoding_to_windows_codepage(string_view) // LCOV_EXCL_LINE + inline int encoding_to_windows_codepage(core::string_view) // LCOV_EXCL_LINE { return -1; // LCOV_EXCL_LINE } diff --git a/src/util/foreach_char.hpp b/src/util/foreach_char.hpp index 37328e12..1c0dc35f 100644 --- a/src/util/foreach_char.hpp +++ b/src/util/foreach_char.hpp @@ -9,7 +9,7 @@ #include -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t # define BOOST_LOCALE_FOREACH_CHAR_I_CHAR8_T(F) F(char8_t) # define BOOST_LOCALE_FOREACH_CHAR_I2_CHAR8_T(F) F(char8_t) #elif defined(__cpp_char8_t) diff --git a/src/util/numeric.hpp b/src/util/numeric.hpp index 146309e3..e54c95c5 100644 --- a/src/util/numeric.hpp +++ b/src/util/numeric.hpp @@ -10,11 +10,8 @@ #include #include #include -#include -#include #include #include -#include #include #include #include @@ -23,22 +20,6 @@ #include "timezone.hpp" namespace boost { namespace locale { namespace util { - - inline bool try_to_int(const std::string& s, int& res) - { - if(s.empty()) - return false; - errno = 0; - char* end_char{}; - const auto v = std::strtol(s.c_str(), &end_char, 10); - if(errno == ERANGE || end_char != s.c_str() + s.size()) - return false; - if(v < std::numeric_limits::min() || v > std::numeric_limits::max()) - return false; - res = v; - return true; - } - template struct formatting_size_traits { static size_t size(const std::basic_string& s, const std::locale& /*l*/) { return s.size(); } @@ -175,11 +156,11 @@ namespace boost { namespace locale { namespace util { iter_type format_time(iter_type out, std::ios_base& ios, CharType fill, std::time_t time, const string_type& format) const { - std::string tz = ios_info::get(ios).time_zone(); - std::tm tm; -#if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__) - std::vector tmp_buf(tz.c_str(), tz.c_str() + tz.size() + 1); + const std::string& tz = ios_info::get(ios).time_zone(); +#if BOOST_OS_BSD_FREE || defined(__APPLE__) + std::vector tz_nonconst; #endif + std::tm tm; if(tz.empty()) { #ifdef BOOST_WINDOWS // Windows uses TLS @@ -200,8 +181,13 @@ namespace boost { namespace locale { namespace util { #if BOOST_OS_LINUX || BOOST_OS_BSD_FREE || defined(__APPLE__) // These have extra fields to specify timezone if(gmtoff != 0) { +# if BOOST_OS_BSD_FREE || defined(__APPLE__) // bsd and apple want tm_zone be non-const - tm.tm_zone = tmp_buf.data(); + tz_nonconst.assign(tz.begin(), tz.end()); + tm.tm_zone = tz_nonconst.data(); +# else + tm.tm_zone = tz.data(); +# endif tm.tm_gmtoff = gmtoff; } #endif diff --git a/src/util/numeric_conversion.hpp b/src/util/numeric_conversion.hpp new file mode 100644 index 00000000..3efc50f5 --- /dev/null +++ b/src/util/numeric_conversion.hpp @@ -0,0 +1,29 @@ +// +// Copyright (c) 2024-2025 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#ifndef BOOST_LOCALE_IMPL_UTIL_NUMERIC_CONVERSIONS_HPP +#define BOOST_LOCALE_IMPL_UTIL_NUMERIC_CONVERSIONS_HPP + +#include +#include +#include + +namespace boost { namespace locale { namespace util { + + template + bool try_to_int(core::string_view s, Integer& value) + { + if(s.size() >= 2 && s[0] == '+') { + if(s[1] == '-') // "+-" is not allowed, invalid "+" is detected by parser + return false; + s.remove_prefix(1); + } + const auto res = boost::charconv::from_chars(s, value); + return res && res.ptr == (s.data() + s.size()); + } +}}} // namespace boost::locale::util + +#endif diff --git a/src/win32/converter.cpp b/src/win32/converter.cpp index 455fef6c..fe168df7 100644 --- a/src/win32/converter.cpp +++ b/src/win32/converter.cpp @@ -62,7 +62,7 @@ namespace boost { namespace locale { namespace impl_win { case char_facet_t::nochar: break; case char_facet_t::char_f: return std::locale(in, new utf8_converter(lc)); case char_facet_t::wchar_f: return std::locale(in, new wide_converter(lc)); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t case char_facet_t::char8_f: return std::locale(in, new utf8_converter(lc)); #elif defined(__cpp_char8_t) case char_facet_t::char8_f: break; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 10284197..a976d009 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,4 +1,4 @@ -# Copyright 2022 Alexander Grund +# Copyright 2022-2024 Alexander Grund # Distributed under the Boost Software License, Version 1.0. # https://www.boost.org/LICENSE_1_0.txt @@ -32,6 +32,7 @@ if(NOT BOOST_LOCALE_ENABLE_POSIX) endif() boost_test_jamfile(FILE Jamfile.v2) +target_link_libraries(boost_locale-test_util_numeric_convert Boost::charconv) # Those require to be run in the test directory foreach(name test_formatting test_message) diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e3b85592..f673b9d6 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -37,6 +37,7 @@ run test_catalog.cpp ; run test_encoding.cpp ; run test_utf.cpp ; run test_util.cpp test_helpers.cpp ; +run test_util_numeric_convert.cpp ; run test_date_time.cpp ; run test_ios_info.cpp ; run test_ios_prop.cpp ; diff --git a/test/boostLocale/test/tools.hpp b/test/boostLocale/test/tools.hpp index 7e9a0453..6537aa5b 100644 --- a/test/boostLocale/test/tools.hpp +++ b/test/boostLocale/test/tools.hpp @@ -113,7 +113,7 @@ std::basic_string to(const std::string& utf8) return out; } -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t template<> std::basic_string to(const std::string& utf8) { diff --git a/test/boostLocale/test/unit_test.hpp b/test/boostLocale/test/unit_test.hpp index cbfc8cd4..366fe838 100644 --- a/test/boostLocale/test/unit_test.hpp +++ b/test/boostLocale/test/unit_test.hpp @@ -88,28 +88,24 @@ namespace boost { namespace locale { namespace test { if(++boost::locale::test::results().error_counter > BOOST_LOCALE_ERROR_LIMIT) throw std::runtime_error("Error limits reached, stopping unit test"); } + + template + bool test_impl(const char* expr, const char* file, int line, const bool v) + { + boost::locale::test::results().test_counter++; + if(!v) { + boost::locale::test::report_error(expr, file, line); + throw std::runtime_error("Critical test " + std::string(expr) + " failed"); + } + return v; + } }}} // namespace boost::locale::test #define BOOST_LOCALE_TEST_REPORT_ERROR(expr) boost::locale::test::report_error(expr, __FILE__, __LINE__) -#define TEST(X) \ - do { \ - boost::locale::test::results().test_counter++; \ - if(X) \ - break; \ - BOOST_LOCALE_TEST_REPORT_ERROR(#X); \ - BOOST_LOCALE_START_CONST_CONDITION \ - } while(0) BOOST_LOCALE_END_CONST_CONDITION +#define TEST(X) (::boost::locale::test::test_impl(#X, __FILE__, __LINE__, (X) ? true : false)) -#define TEST_REQUIRE(X) \ - do { \ - boost::locale::test::results().test_counter++; \ - if(X) \ - break; \ - BOOST_LOCALE_TEST_REPORT_ERROR(#X); \ - throw std::runtime_error("Critical test " #X " failed"); \ - BOOST_LOCALE_START_CONST_CONDITION \ - } while(0) BOOST_LOCALE_END_CONST_CONDITION +#define TEST_REQUIRE(X) (::boost::locale::test::test_impl(#X, __FILE__, __LINE__, (X) ? true : false)) #define TEST_THROWS(X, E) \ do { \ diff --git a/test/formatting_common.hpp b/test/formatting_common.hpp index 260bc20d..179efd3c 100644 --- a/test/formatting_common.hpp +++ b/test/formatting_common.hpp @@ -30,37 +30,43 @@ void test_parse_multi_number_by_char(const std::locale& locale) stream >> boost::locale::as::number; IntType value; - TEST_REQUIRE(stream >> value); - TEST_EQ(value, IntType(42)); - TEST_EQ(static_cast(stream.get()), '.'); - TEST_REQUIRE(stream >> value); - TEST_EQ(value, expectedInt); - TEST_REQUIRE(!(stream >> value)); - TEST(stream.eof()); + if TEST(stream >> value) { + TEST_EQ(value, IntType(42)); + TEST_EQ(static_cast(stream.get()), '.'); + if TEST(stream >> value) { + TEST_EQ(value, expectedInt); + if TEST(!(stream >> value)) + TEST(stream.eof()); + } + } stream.str(ascii_to("42.25,678")); stream.clear(); float fValue; - TEST_REQUIRE(stream >> fValue); - TEST_EQ(fValue, 42.25); - TEST_EQ(static_cast(stream.get()), ','); - TEST_REQUIRE(stream >> value); - TEST_EQ(value, IntType(678)); - TEST_REQUIRE(!(stream >> value)); - TEST(stream.eof()); + if TEST(stream >> fValue) { + TEST_EQ(fValue, 42.25); + TEST_EQ(static_cast(stream.get()), ','); + if TEST(stream >> value) { + TEST_EQ(value, IntType(678)); + if TEST(!(stream >> value)) + TEST(stream.eof()); + } + } // Parsing a floating point currency to integer truncates the floating point value but fully parses it stream.str(ascii_to("USD1,234.55,67.89")); stream.clear(); - TEST_REQUIRE(!(stream >> value)); - stream.clear(); - stream >> boost::locale::as::currency >> boost::locale::as::currency_iso; - if(stream >> value) { // Parsing currencies not fully supported by WinAPI backend - TEST_EQ(value, IntType(1234)); - TEST_EQ(static_cast(stream.get()), ','); - TEST_REQUIRE(stream >> boost::locale::as::number >> value); - TEST_EQ(value, IntType(67)); - TEST(!stream.eof()); + if TEST(!(stream >> value)) { + stream.clear(); + stream >> boost::locale::as::currency >> boost::locale::as::currency_iso; + if(stream >> value) { // Parsing currencies not fully supported by WinAPI backend + TEST_EQ(value, IntType(1234)); + TEST_EQ(static_cast(stream.get()), ','); + if TEST(stream >> boost::locale::as::number >> value) { + TEST_EQ(value, IntType(67)); + TEST(!stream.eof()); + } + } } } diff --git a/test/show_config.cpp b/test/show_config.cpp index 226ec1cb..e2dd8541 100644 --- a/test/show_config.cpp +++ b/test/show_config.cpp @@ -65,7 +65,7 @@ void test_main(int /*argc*/, char** /*argv*/) std::cout << "no\n"; #endif std::cout << "- std::basic_string: "; -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << "yes\n"; #elif defined(__cpp_lib_char8_t) std::cout << "partial\n"; diff --git a/test/test_boundary.cpp b/test/test_boundary.cpp index e4e0ba4c..64b1b766 100644 --- a/test/test_boundary.cpp +++ b/test/test_boundary.cpp @@ -37,9 +37,10 @@ void run_segment_iterator_test(const lb::segment_index& map, unsigned i = 0; typename lb::segment_index::iterator p; for(p = map.begin(); p != map.end(); ++p, i++) { - TEST_REQUIRE(i < masks.size()); - TEST_EQ(p->str(), chunks[i]); - TEST_EQ(p->rule(), masks[i]); + if TEST(i < masks.size()) { + TEST_EQ(p->str(), chunks[i]); + TEST_EQ(p->rule(), masks[i]); + } } TEST_EQ(i, chunks.size()); @@ -88,9 +89,10 @@ void run_break_iterator_test(const lb::boundary_point_index& map, unsigned i = 0; typename lb::boundary_point_index::iterator p; for(p = map.begin(); p != map.end(); ++p, i++) { - TEST_REQUIRE(i < masks.size()); - TEST(p->iterator() == iters[i]); - TEST_EQ(p->rule(), masks[i]); + if TEST(i < masks.size()) { + TEST(p->iterator() == iters[i]); + TEST_EQ(p->rule(), masks[i]); + } } TEST_EQ(i, iters.size()); @@ -117,12 +119,13 @@ void verify_index(const lb::boundary_point_index& map, const masks_t& masks) { BOOST_ASSERT(iters.size() == masks.size()); - TEST_REQUIRE(static_cast(std::distance(map.begin(), map.end())) == masks.size()); - size_t i = 0; - for(const auto& b_point : map) { - TEST(b_point.iterator() == iters[i]); - TEST_EQ(b_point.rule(), masks[i]); - ++i; + if TEST(static_cast(std::distance(map.begin(), map.end())) == masks.size()) { + size_t i = 0; + for(const auto& b_point : map) { + TEST(b_point.iterator() == iters[i]); + TEST_EQ(b_point.rule(), masks[i]); + ++i; + } } } @@ -130,12 +133,13 @@ template void verify_index(const lb::segment_index& map, const chunks_t& chunks, const masks_t& masks) { BOOST_ASSERT(chunks.size() == masks.size()); - TEST_REQUIRE(static_cast(std::distance(map.begin(), map.end())) == masks.size()); - size_t i = 0; - for(const auto& seg : map) { - TEST_EQ(seg.str(), chunks[i]); - TEST_EQ(seg.rule(), masks[i]); - ++i; + if TEST(static_cast(std::distance(map.begin(), map.end())) == masks.size()) { + size_t i = 0; + for(const auto& seg : map) { + TEST_EQ(seg.str(), chunks[i]); + TEST_EQ(seg.rule(), masks[i]); + ++i; + } } } @@ -306,7 +310,7 @@ void test_boundaries(std::string* all, int* first, int* second, lb::boundary_typ run_word(all, first, second, nullptr, nullptr, nullptr, g("he_IL.cp1255"), t); std::cout << " wchar_t" << std::endl; run_word(all, first, second, nullptr, nullptr, nullptr, g("he_IL.UTF-8"), t); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; run_word(all, first, second, nullptr, nullptr, nullptr, g("he_IL.UTF-8"), t); #endif @@ -369,7 +373,7 @@ void word_boundary() run_word(txt_simple, none_simple, zero, word_simple, zero, zero, utf8_en_locale); run_word(txt_all, none_all, num_all, word_all, kana_all, ideo_all, utf8_jp_locale); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; run_word(txt_empty, zero, zero, zero, zero, zero, g("ja_JP.UTF-8")); run_word(txt_simple, none_simple, zero, word_simple, zero, zero, utf8_en_locale); diff --git a/test/test_codepage_converter.cpp b/test/test_codepage_converter.cpp index 458ab217..021a5b02 100644 --- a/test/test_codepage_converter.cpp +++ b/test/test_codepage_converter.cpp @@ -86,167 +86,167 @@ void test_main(int /*argc*/, char** /*argv*/) TEST(!create_simple_converter("UTF-8")); std::unique_ptr cvt = create_utf8_converter(); - TEST_REQUIRE(cvt); - TEST(cvt->is_thread_safe()); - TEST_EQ(cvt->max_len(), 4); - - std::cout << "-- Correct" << std::endl; - - TEST_TO("\x7f", 0x7f); - TEST_TO("\xC2\x80", 0x80); - TEST_TO("\xdf\xBF", 0x7FF); - TEST_TO("\xe0\xa0\x80", 0x800); - TEST_TO("\xef\xbf\xbf", 0xFFFF); - TEST_TO("\xf0\x90\x80\x80", 0x10000); - TEST_TO("\xf4\x8f\xbf\xbf", 0x10FFFF); - - std::cout << "-- Too big" << std::endl; - TEST_TO("\xf4\x9f\x80\x80", illegal); // 11 0000 - TEST_TO("\xfb\xbf\xbf\xbf", illegal); // 3FF FFFF - TEST_TO("\xf8\x90\x80\x80\x80", illegal); // 400 0000 - TEST_TO("\xfd\xbf\xbf\xbf\xbf\xbf", illegal); // 7fff ffff - - std::cout << "-- Invalid trail" << std::endl; - TEST_TO("\xC2\x7F", illegal); - TEST_TO("\xdf\x7F", illegal); - TEST_TO("\xe0\x7F\x80", illegal); - TEST_TO("\xef\xbf\x7F", illegal); - TEST_TO("\xe0\x7F\x80", illegal); - TEST_TO("\xef\xbf\x7F", illegal); - TEST_TO("\xf0\x7F\x80\x80", illegal); - TEST_TO("\xf4\x7f\xbf\xbf", illegal); - TEST_TO("\xf0\x90\x7F\x80", illegal); - TEST_TO("\xf4\x8f\x7F\xbf", illegal); - TEST_TO("\xf0\x90\x80\x7F", illegal); - TEST_TO("\xf4\x8f\xbf\x7F", illegal); - - std::cout << "-- Invalid length" << std::endl; - - // Test that this actually works - TEST_TO(make2(0x80), 0x80); - TEST_TO(make2(0x7ff), 0x7ff); - - TEST_TO(make3(0x800), 0x800); - TEST_TO(make3(0xffff), 0xffff); - - TEST_TO(make4(0x10000), 0x10000); - TEST_TO(make4(0x10ffff), 0x10ffff); - - TEST_TO(make4(0x110000), illegal); - TEST_TO(make4(0x1fffff), illegal); - - TEST_TO(make2(0), illegal); - TEST_TO(make3(0), illegal); - TEST_TO(make4(0), illegal); - TEST_TO(make2(0x7f), illegal); - TEST_TO(make3(0x7f), illegal); - TEST_TO(make4(0x7f), illegal); - - TEST_TO(make3(0x80), illegal); - TEST_TO(make4(0x80), illegal); - TEST_TO(make3(0x7ff), illegal); - TEST_TO(make4(0x7ff), illegal); - - TEST_TO(make4(0x8000), illegal); - TEST_TO(make4(0xffff), illegal); - - std::cout << "-- Invalid surrogate" << std::endl; - - TEST_TO(make3(0xD800), illegal); - TEST_TO(make3(0xDBFF), illegal); - TEST_TO(make3(0xDC00), illegal); - TEST_TO(make3(0xDFFF), illegal); - - TEST_TO(make4(0xD800), illegal); - TEST_TO(make4(0xDBFF), illegal); - TEST_TO(make4(0xDC00), illegal); - TEST_TO(make4(0xDFFF), illegal); - - std::cout << "-- Incomplete" << std::endl; - - TEST_TO("\x80", illegal); - TEST_TO("\xC2", incomplete); - - TEST_TO("\xdf", incomplete); - - TEST_TO("\xe0", incomplete); - TEST_TO("\xe0\xa0", incomplete); - - TEST_TO("\xef\xbf", incomplete); - TEST_TO("\xef", incomplete); - - TEST_TO("\xf0\x90\x80", incomplete); - TEST_TO("\xf0\x90", incomplete); - TEST_TO("\xf0", incomplete); - - TEST_TO("\xf4\x8f\xbf", incomplete); - TEST_TO("\xf4\x8f", incomplete); - TEST_TO("\xf4", incomplete); - - std::cout << "- To UTF-8\n"; - - std::cout << "-- Test correct" << std::endl; - - TEST_FROM("\x7f", 0x7f); - TEST_FROM("\xC2\x80", 0x80); - TEST_FROM("\xdf\xBF", 0x7FF); - TEST_INC(0x7FF, 1); - TEST_FROM("\xe0\xa0\x80", 0x800); - TEST_INC(0x800, 2); - TEST_INC(0x800, 1); - TEST_FROM("\xef\xbf\xbf", 0xFFFF); - TEST_INC(0x10000, 3); - TEST_INC(0x10000, 2); - TEST_INC(0x10000, 1); - TEST_FROM("\xf0\x90\x80\x80", 0x10000); - TEST_FROM("\xf4\x8f\xbf\xbf", 0x10FFFF); - - std::cout << "-- Test no surrogate " << std::endl; - - TEST_FROM(nullptr, 0xD800); - TEST_FROM(nullptr, 0xDBFF); - TEST_FROM(nullptr, 0xDC00); - TEST_FROM(nullptr, 0xDFFF); - - std::cout << "-- Test invalid " << std::endl; - - TEST_FROM(nullptr, 0x110000); - TEST_FROM(nullptr, 0x1FFFFF); - + if TEST(cvt) { + TEST(cvt->is_thread_safe()); + TEST_EQ(cvt->max_len(), 4); + + std::cout << "-- Correct" << std::endl; + + TEST_TO("\x7f", 0x7f); + TEST_TO("\xC2\x80", 0x80); + TEST_TO("\xdf\xBF", 0x7FF); + TEST_TO("\xe0\xa0\x80", 0x800); + TEST_TO("\xef\xbf\xbf", 0xFFFF); + TEST_TO("\xf0\x90\x80\x80", 0x10000); + TEST_TO("\xf4\x8f\xbf\xbf", 0x10FFFF); + + std::cout << "-- Too big" << std::endl; + TEST_TO("\xf4\x9f\x80\x80", illegal); // 11 0000 + TEST_TO("\xfb\xbf\xbf\xbf", illegal); // 3FF FFFF + TEST_TO("\xf8\x90\x80\x80\x80", illegal); // 400 0000 + TEST_TO("\xfd\xbf\xbf\xbf\xbf\xbf", illegal); // 7fff ffff + + std::cout << "-- Invalid trail" << std::endl; + TEST_TO("\xC2\x7F", illegal); + TEST_TO("\xdf\x7F", illegal); + TEST_TO("\xe0\x7F\x80", illegal); + TEST_TO("\xef\xbf\x7F", illegal); + TEST_TO("\xe0\x7F\x80", illegal); + TEST_TO("\xef\xbf\x7F", illegal); + TEST_TO("\xf0\x7F\x80\x80", illegal); + TEST_TO("\xf4\x7f\xbf\xbf", illegal); + TEST_TO("\xf0\x90\x7F\x80", illegal); + TEST_TO("\xf4\x8f\x7F\xbf", illegal); + TEST_TO("\xf0\x90\x80\x7F", illegal); + TEST_TO("\xf4\x8f\xbf\x7F", illegal); + + std::cout << "-- Invalid length" << std::endl; + + // Test that this actually works + TEST_TO(make2(0x80), 0x80); + TEST_TO(make2(0x7ff), 0x7ff); + + TEST_TO(make3(0x800), 0x800); + TEST_TO(make3(0xffff), 0xffff); + + TEST_TO(make4(0x10000), 0x10000); + TEST_TO(make4(0x10ffff), 0x10ffff); + + TEST_TO(make4(0x110000), illegal); + TEST_TO(make4(0x1fffff), illegal); + + TEST_TO(make2(0), illegal); + TEST_TO(make3(0), illegal); + TEST_TO(make4(0), illegal); + TEST_TO(make2(0x7f), illegal); + TEST_TO(make3(0x7f), illegal); + TEST_TO(make4(0x7f), illegal); + + TEST_TO(make3(0x80), illegal); + TEST_TO(make4(0x80), illegal); + TEST_TO(make3(0x7ff), illegal); + TEST_TO(make4(0x7ff), illegal); + + TEST_TO(make4(0x8000), illegal); + TEST_TO(make4(0xffff), illegal); + + std::cout << "-- Invalid surrogate" << std::endl; + + TEST_TO(make3(0xD800), illegal); + TEST_TO(make3(0xDBFF), illegal); + TEST_TO(make3(0xDC00), illegal); + TEST_TO(make3(0xDFFF), illegal); + + TEST_TO(make4(0xD800), illegal); + TEST_TO(make4(0xDBFF), illegal); + TEST_TO(make4(0xDC00), illegal); + TEST_TO(make4(0xDFFF), illegal); + + std::cout << "-- Incomplete" << std::endl; + + TEST_TO("\x80", illegal); + TEST_TO("\xC2", incomplete); + + TEST_TO("\xdf", incomplete); + + TEST_TO("\xe0", incomplete); + TEST_TO("\xe0\xa0", incomplete); + + TEST_TO("\xef\xbf", incomplete); + TEST_TO("\xef", incomplete); + + TEST_TO("\xf0\x90\x80", incomplete); + TEST_TO("\xf0\x90", incomplete); + TEST_TO("\xf0", incomplete); + + TEST_TO("\xf4\x8f\xbf", incomplete); + TEST_TO("\xf4\x8f", incomplete); + TEST_TO("\xf4", incomplete); + + std::cout << "- To UTF-8\n"; + + std::cout << "-- Test correct" << std::endl; + + TEST_FROM("\x7f", 0x7f); + TEST_FROM("\xC2\x80", 0x80); + TEST_FROM("\xdf\xBF", 0x7FF); + TEST_INC(0x7FF, 1); + TEST_FROM("\xe0\xa0\x80", 0x800); + TEST_INC(0x800, 2); + TEST_INC(0x800, 1); + TEST_FROM("\xef\xbf\xbf", 0xFFFF); + TEST_INC(0x10000, 3); + TEST_INC(0x10000, 2); + TEST_INC(0x10000, 1); + TEST_FROM("\xf0\x90\x80\x80", 0x10000); + TEST_FROM("\xf4\x8f\xbf\xbf", 0x10FFFF); + + std::cout << "-- Test no surrogate " << std::endl; + + TEST_FROM(nullptr, 0xD800); + TEST_FROM(nullptr, 0xDBFF); + TEST_FROM(nullptr, 0xDC00); + TEST_FROM(nullptr, 0xDFFF); + + std::cout << "-- Test invalid " << std::endl; + + TEST_FROM(nullptr, 0x110000); + TEST_FROM(nullptr, 0x1FFFFF); + } std::cout << "Test windows-1255" << std::endl; cvt = create_simple_converter("windows-1255"); - TEST_REQUIRE(cvt); - TEST(cvt->is_thread_safe()); - TEST_EQ(cvt->max_len(), 1); - - std::cout << "- From 1255" << std::endl; + if TEST(cvt) { + TEST(cvt->is_thread_safe()); + TEST_EQ(cvt->max_len(), 1); - TEST_TO("\xa4", 0x20aa); - TEST_TO("\xe0", 0x05d0); - TEST_TO("\xc4", 0x5b4); - TEST_TO("\xfb", illegal); - TEST_TO("\xdd", illegal); - TEST_TO("\xff", illegal); - TEST_TO("\xfe", 0x200f); + std::cout << "- From 1255" << std::endl; - std::cout << "- To 1255" << std::endl; + TEST_TO("\xa4", 0x20aa); + TEST_TO("\xe0", 0x05d0); + TEST_TO("\xc4", 0x5b4); + TEST_TO("\xfb", illegal); + TEST_TO("\xdd", illegal); + TEST_TO("\xff", illegal); + TEST_TO("\xfe", 0x200f); - TEST_FROM("\xa4", 0x20aa); - TEST_FROM("\xe0", 0x05d0); - TEST_FROM("\xc4", 0x5b4); - TEST_FROM("\xfe", 0x200f); + std::cout << "- To 1255" << std::endl; - TEST_FROM(nullptr, 0xe4); - TEST_FROM(nullptr, 0xd0); + TEST_FROM("\xa4", 0x20aa); + TEST_FROM("\xe0", 0x05d0); + TEST_FROM("\xc4", 0x5b4); + TEST_FROM("\xfe", 0x200f); + TEST_FROM(nullptr, 0xe4); + TEST_FROM(nullptr, 0xd0); + } #ifdef BOOST_LOCALE_WITH_ICU std::cout << "Testing Shift-JIS using ICU/uconv" << std::endl; cvt = boost::locale::impl_icu::create_uconv_converter("Shift-JIS"); - TEST_REQUIRE(cvt); - test_shiftjis(cvt); + if TEST(cvt) + test_shiftjis(cvt); #endif std::cout << "Testing Shift-JIS using POSIX/iconv" << std::endl; diff --git a/test/test_convert.cpp b/test/test_convert.cpp index 8752d747..d00568f1 100644 --- a/test/test_convert.cpp +++ b/test/test_convert.cpp @@ -25,7 +25,7 @@ void test_norm(std::string orig, std::string normal, boost::locale::norm_type ty { test_normc(orig, normal, type); test_normc(to(orig), to(normal), type); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t test_normc(to(orig), to(normal), type); #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T @@ -105,7 +105,7 @@ void test_main(int /*argc*/, char** /*argv*/) TEST_ALL_CASES; #undef TEST_V -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t # define TEST_V(how, source_s, dest_s) TEST_A(char8_t, how, to(source_s), to(dest_s)) TEST_ALL_CASES; # undef TEST_V diff --git a/test/test_encoding.cpp b/test/test_encoding.cpp index 0a01d43d..6f1dcc60 100644 --- a/test/test_encoding.cpp +++ b/test/test_encoding.cpp @@ -425,7 +425,7 @@ void test_utf_to_utf_for() test_from_utf_for_impls(utf(utf8_string), utf8_string, "UTF-8"); std::cout << "---- wchar_t\n"; test_utf_to_utf_for(utf8_string); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << "---- char8_t\n"; test_utf_to_utf_for(utf8_string); #endif @@ -446,7 +446,7 @@ void test_utf_to_utf() test_utf_to_utf_for(); std::cout << "-- wchar_t\n"; test_utf_to_utf_for(); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << "-- char8_t\n"; test_utf_to_utf_for(); #endif @@ -643,7 +643,7 @@ void test_latin1_conversions() test_latin1_conversions_for(); std::cout << "-- wchar_t\n"; test_latin1_conversions_for(); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << "-- char8_t\n"; test_latin1_conversions_for(); #endif @@ -781,7 +781,7 @@ void test_main(int /*argc*/, char** /*argv*/) test_utf_for(); std::cout << " wchar_t" << std::endl; test_utf_for(); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; test_utf_for(); #endif diff --git a/test/test_generator.cpp b/test/test_generator.cpp index ed3ac3cb..88bfb876 100644 --- a/test/test_generator.cpp +++ b/test/test_generator.cpp @@ -329,7 +329,7 @@ void test_main(int /*argc*/, char** /*argv*/) #else # define TEST_FOR_CHAR8(check) (void)0 #endif -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t # define TEST_FOR_STRING8(check) TEST(check) #else # define TEST_FOR_STRING8(check) (void)0 diff --git a/test/test_ios_info.cpp b/test/test_ios_info.cpp index 79179a8f..bcffe3d6 100644 --- a/test/test_ios_info.cpp +++ b/test/test_ios_info.cpp @@ -222,7 +222,7 @@ void test_any_string() TEST_EQ(s.get(), ascii_to("Char32 Pattern")); TEST_THROWS(s.get(), std::bad_cast); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t s.set(ascii_to("Char8 Pattern")); TEST_EQ(s.get(), ascii_to("Char8 Pattern")); TEST_THROWS(s.get(), std::bad_cast); diff --git a/test/test_message.cpp b/test/test_message.cpp index 304d66c7..80ea7d77 100644 --- a/test/test_message.cpp +++ b/test/test_message.cpp @@ -84,7 +84,7 @@ std::wstring same_w(std::wstring s) return s; } -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::basic_string same_u8(std::basic_string s) { return s; @@ -376,7 +376,7 @@ void test_cntranslate(const std::string& c, impl::test_cntranslate(c, s, p, n, expected, l, domain); std::cout << " wchar_t" << std::endl; impl::test_cntranslate(c, s, p, n, expected, l, domain); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; impl::test_cntranslate(c, s, p, n, expected, l, domain); #endif @@ -401,7 +401,7 @@ void test_ntranslate(const std::string& s, impl::test_ntranslate(s, p, n, expected, l, domain); std::cout << " wchar_t" << std::endl; impl::test_ntranslate(s, p, n, expected, l, domain); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; impl::test_ntranslate(s, p, n, expected, l, domain); #endif @@ -425,7 +425,7 @@ void test_ctranslate(const std::string& c, impl::test_ctranslate(c, original, expected, l, domain); std::cout << " wchar_t" << std::endl; impl::test_ctranslate(c, original, expected, l, domain); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; impl::test_ctranslate(c, original, expected, l, domain); #endif @@ -448,7 +448,7 @@ void test_translate(const std::string& original, impl::test_translate(original, expected, l, domain); std::cout << " wchar_t" << std::endl; impl::test_translate(original, expected, l, domain); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << " char8_t" << std::endl; impl::test_translate(original, expected, l, domain); #endif @@ -570,7 +570,7 @@ void test_main(int argc, char** argv) TEST_EQ(same_s(bl::translate("hello")), "שלום"); TEST_EQ(same_w(bl::translate(to("hello"))), to("שלום")); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t TEST_EQ(same_u8(bl::translate(to("hello"))), to("שלום")); #endif #ifdef BOOST_LOCALE_ENABLE_CHAR16_T diff --git a/test/test_posix_convert.cpp b/test/test_posix_convert.cpp index 616ebbc7..9d2b3e7a 100644 --- a/test/test_posix_convert.cpp +++ b/test/test_posix_convert.cpp @@ -52,13 +52,14 @@ void test_char() else { std::cout << "Testing " << name << std::endl; locale_holder cl(newlocale(LC_ALL_MASK, name.c_str(), nullptr)); - TEST_REQUIRE(cl); + if TEST(cl) { #ifndef BOOST_LOCALE_NO_POSIX_BACKEND - if(towupper_l(L'i', cl) == 0x130) - test_one(gen(name), "i", "i", "İ"); - else - std::cout << " Turkish locale is not supported well" << std::endl; // LCOV_EXCL_LINE + if(towupper_l(L'i', cl) == 0x130) + test_one(gen(name), "i", "i", "İ"); + else + std::cout << " Turkish locale is not supported well" << std::endl; // LCOV_EXCL_LINE #endif + } } } diff --git a/test/test_posix_formatting.cpp b/test/test_posix_formatting.cpp index 15c4d6d2..e96a0e24 100644 --- a/test/test_posix_formatting.cpp +++ b/test/test_posix_formatting.cpp @@ -164,13 +164,13 @@ void test_main(int /*argc*/, char** /*argv*/) else { std::locale generated_locale = gen(locale_name); locale_holder real_locale(newlocale(LC_ALL_MASK, locale_name.c_str(), nullptr)); - TEST_REQUIRE(real_locale); + if TEST(real_locale) { + std::cout << "UTF-8" << std::endl; + test_by_char(generated_locale, real_locale); - std::cout << "UTF-8" << std::endl; - test_by_char(generated_locale, real_locale); - - std::cout << "Wide UTF-" << sizeof(wchar_t) * 8 << std::endl; - test_by_char(generated_locale, real_locale); + std::cout << "Wide UTF-" << sizeof(wchar_t) * 8 << std::endl; + test_by_char(generated_locale, real_locale); + } } } { diff --git a/test/test_std_convert.cpp b/test/test_std_convert.cpp index bc8126e3..1b8537a2 100644 --- a/test/test_std_convert.cpp +++ b/test/test_std_convert.cpp @@ -84,7 +84,7 @@ void test_main(int /*argc*/, char** /*argv*/) test_char(); std::cout << "Testing wchar_t" << std::endl; test_char(); -#ifndef BOOST_LOCALE_NO_CXX20_STRING8 +#ifdef __cpp_lib_char8_t std::cout << "Testing char8_t" << std::endl; test_char(); #endif diff --git a/test/test_util.cpp b/test/test_util.cpp index caa5983e..1c4145ab 100644 --- a/test/test_util.cpp +++ b/test/test_util.cpp @@ -37,31 +37,33 @@ void test_hold_ptr() auto* raw = new Dummy(42); boost::locale::hold_ptr ptr(raw); const boost::locale::hold_ptr& const_ptr = ptr; - TEST_REQUIRE(ptr); - TEST(ptr.get() == raw); - TEST(const_ptr.get() == raw); - // const propagation - TEST_EQ((*ptr).foo(), raw->i_); - TEST_EQ((*const_ptr).foo(), -raw->i_); - TEST_EQ(ptr->foo(), raw->i_); - TEST_EQ(const_ptr->foo(), -raw->i_); - TEST_EQ(ptr.get()->foo(), raw->i_); - TEST_EQ(const_ptr.get()->foo(), -raw->i_); - // move construct - boost::locale::hold_ptr ptr2 = std::move(ptr); - TEST(!ptr); - TEST_REQUIRE(ptr2); - TEST(ptr2.get() == raw); - // move assign - ptr = std::move(ptr2); - TEST(ptr); - TEST_REQUIRE(!ptr2); - TEST(ptr.get() == raw); - // Swap - boost::locale::hold_ptr ptr3(new Dummy(1337)); - ptr.swap(ptr3); - TEST_EQ(ptr->foo(), 1337); - TEST_EQ(ptr3->foo(), 42); + if TEST(ptr) { + TEST(ptr.get() == raw); + TEST(const_ptr.get() == raw); + // const propagation + TEST_EQ((*ptr).foo(), raw->i_); + TEST_EQ((*const_ptr).foo(), -raw->i_); + TEST_EQ(ptr->foo(), raw->i_); + TEST_EQ(const_ptr->foo(), -raw->i_); + TEST_EQ(ptr.get()->foo(), raw->i_); + TEST_EQ(const_ptr.get()->foo(), -raw->i_); + // move construct + boost::locale::hold_ptr ptr2 = std::move(ptr); + TEST(!ptr); + if TEST(ptr2) { + TEST(ptr2.get() == raw); + // move assign + ptr = std::move(ptr2); + TEST(ptr); + TEST(!ptr2); + TEST(ptr.get() == raw); + // Swap + boost::locale::hold_ptr ptr3(new Dummy(1337)); + ptr.swap(ptr3); + TEST_EQ(ptr->foo(), 1337); + TEST_EQ(ptr3->foo(), 42); + } + } } TEST_EQ(Dummy::ctr, 0); auto* raw = new Dummy(42); @@ -370,50 +372,9 @@ void test_locale_data() verify_against_icu(); } -#include "../src/util/numeric.hpp" -#include -#include -#include - -void test_try_to_int() -{ - using boost::locale::util::try_to_int; - - int v = 1337; - TEST(try_to_int("0", v)); - TEST_EQ(v, 0); - - TEST(try_to_int("42", v)); - TEST_EQ(v, 42); - - TEST(try_to_int("-1337", v)); - TEST_EQ(v, -1337); - - std::ostringstream ss; - ss.imbue(std::locale::classic()); - empty_stream(ss) << std::numeric_limits::min(); - TEST(try_to_int(ss.str(), v)); - TEST_EQ(v, std::numeric_limits::min()); - empty_stream(ss) << std::numeric_limits::max(); - TEST(try_to_int(ss.str(), v)); - TEST_EQ(v, std::numeric_limits::max()); - - TEST(!try_to_int("", v)); - TEST(!try_to_int("a", v)); - TEST(!try_to_int("1.", v)); - TEST(!try_to_int("1a", v)); - TEST(!try_to_int("a1", v)); - static_assert(sizeof(long long) > sizeof(int), "Value below under/overflows!"); - empty_stream(ss) << static_cast(std::numeric_limits::min()) - 1; - TEST(!try_to_int(ss.str(), v)); - empty_stream(ss) << static_cast(std::numeric_limits::max()) + 1; - TEST(!try_to_int(ss.str(), v)); -} - void test_main(int /*argc*/, char** /*argv*/) { test_hold_ptr(); test_get_system_locale(); test_locale_data(); - test_try_to_int(); } diff --git a/test/test_util_numeric_convert.cpp b/test/test_util_numeric_convert.cpp new file mode 100644 index 00000000..c30cfaf0 --- /dev/null +++ b/test/test_util_numeric_convert.cpp @@ -0,0 +1,60 @@ +// +// Copyright (c) 2022-2025 Alexander Grund +// +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include "../src/util/numeric_conversion.hpp" +#include "boostLocale/test/tools.hpp" +#include +#include +#include + +void test_try_to_int() +{ + using boost::locale::util::try_to_int; + + int v = 1337; + if TEST(try_to_int("0", v)) + TEST_EQ(v, 0); + + if TEST(try_to_int("42", v)) + TEST_EQ(v, 42); + + if TEST(try_to_int("-1337", v)) + TEST_EQ(v, -1337); + + if TEST(try_to_int("+1337", v)) + TEST_EQ(v, +1337); + + std::ostringstream ss; + ss.imbue(std::locale::classic()); + empty_stream(ss) << std::numeric_limits::min(); + if TEST(try_to_int(ss.str(), v)) + TEST_EQ(v, std::numeric_limits::min()); + empty_stream(ss) << std::numeric_limits::max(); + if TEST(try_to_int(ss.str(), v)) + TEST_EQ(v, std::numeric_limits::max()); + + TEST(!try_to_int("", v)); + TEST(!try_to_int("+", v)); + TEST(!try_to_int("-", v)); + TEST(!try_to_int("++", v)); + TEST(!try_to_int("+-", v)); + TEST(!try_to_int("++1", v)); + TEST(!try_to_int("+-1", v)); + TEST(!try_to_int("a", v)); + TEST(!try_to_int("1.", v)); + TEST(!try_to_int("1a", v)); + TEST(!try_to_int("a1", v)); + static_assert(sizeof(long long) > sizeof(int), "Value below under/overflows!"); + empty_stream(ss) << static_cast(std::numeric_limits::min()) - 1; + TEST(!try_to_int(ss.str(), v)); + empty_stream(ss) << static_cast(std::numeric_limits::max()) + 1; + TEST(!try_to_int(ss.str(), v)); +} + +void test_main(int /*argc*/, char** /*argv*/) +{ + test_try_to_int(); +}