From 49c5207be439260f15884e286bf2b3c806f06513 Mon Sep 17 00:00:00 2001 From: Foster Brereton Date: Thu, 23 May 2024 17:26:10 -0700 Subject: [PATCH] Adding `to_chars` fallback (#117) * adding `to_chars` fallback * tweak suggested by @thinlang * fixing test --- adobe/string/to_string.hpp | 22 ++++++++++++++++++++++ test/to_string/main.cpp | 18 +++++++++++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/adobe/string/to_string.hpp b/adobe/string/to_string.hpp index 54ef046a..6c401d6f 100644 --- a/adobe/string/to_string.hpp +++ b/adobe/string/to_string.hpp @@ -24,6 +24,19 @@ #include +// `to_chars` floating-point support was added to macOS 13.3. Some ASL +// clients have deployment targets that go back earlier than that. +// In such case, `adobe::to_string` falls back to an implementation +// that relies on the output-iterator variant of `adobe::to_string`, +// pivoting on precise/short based on the exponent of the number +// being serialized. This gets us close to the output of `to_chars` +// without overcomplicating the fallback. +#if defined(_LIBCPP_AVAILABILITY_HAS_NO_TO_CHARS_FLOATING_POINT) + #define ADOBE_HAS_TO_CHARS_FP() 0 +#else + #define ADOBE_HAS_TO_CHARS_FP() 1 +#endif + /**************************************************************************************************/ namespace adobe { @@ -161,6 +174,7 @@ inline std::string to_string(double x) { if (x == std::numeric_limits::infinity()) return "Infinity"; if (x == -std::numeric_limits::infinity()) return "-Infinity"; +#if ADOBE_HAS_TO_CHARS_FP() std::array str; char* first = &str[0]; char* last = first + str.size(); @@ -169,6 +183,14 @@ inline std::string to_string(double x) { return tcr.ec == std::errc() ? std::string(first, tcr.ptr - first) : std::make_error_code(tcr.ec).message(); +#else + std::string result; + double f3; + double f2 = std::modf(x, &f3); + const auto use_precise = std::abs(std::log10(f2)) > 7; + to_string(x, std::back_inserter(result), use_precise); + return result; +#endif } /**************************************************************************************************/ diff --git a/test/to_string/main.cpp b/test/to_string/main.cpp index be40d050..c8eba23c 100644 --- a/test/to_string/main.cpp +++ b/test/to_string/main.cpp @@ -109,6 +109,7 @@ std::string asl_to_string(const test_t& test) { } std::string std_to_chars(const test_t& test) { +#if ADOBE_HAS_TO_CHARS_FP() std::array str; if (auto [ptr, ec] = std::to_chars(str.data(), str.data() + str.size(), test.value_m); ec == std::errc()) { const auto sz = ptr - str.data(); @@ -116,6 +117,9 @@ std::string std_to_chars(const test_t& test) { } else { return std::make_error_code(ec).message(); } +#else + return "unavailable"; +#endif } std::string any_regular_serialization(const test_t& test) { @@ -147,15 +151,23 @@ std::size_t test_suite(std::string (*convert)(const test_t& test), const char* l /******************************************************************************/ BOOST_AUTO_TEST_CASE(serialization_test_suite) { +#if ADOBE_HAS_TO_CHARS_FP() + constexpr auto to_string_match_count_k = 10; + constexpr auto to_chars_match_count_k = 6; +#else + constexpr auto to_string_match_count_k = 9; + constexpr auto to_chars_match_count_k = 0; +#endif + BOOST_CHECK_EQUAL(test_suite(&asl_to_string_v1, "adobe::to_string (precise)"), 3); BOOST_CHECK_EQUAL(test_suite(&asl_to_string_v1, "adobe::to_string (short)"), 2); - BOOST_CHECK_EQUAL(test_suite(&asl_to_string, "adobe::to_string (v2)"), 10); + BOOST_CHECK_EQUAL(test_suite(&asl_to_string, "adobe::to_string (v2)"), to_string_match_count_k); - BOOST_CHECK_EQUAL(test_suite(&std_to_chars, "std::to_chars"), 6); + BOOST_CHECK_EQUAL(test_suite(&std_to_chars, "std::to_chars"), to_chars_match_count_k); - BOOST_CHECK_EQUAL(test_suite(&any_regular_serialization, "adobe::any_regular_t"), 10); + BOOST_CHECK_EQUAL(test_suite(&any_regular_serialization, "adobe::any_regular_t"), to_string_match_count_k); } /******************************************************************************/