[libcxx-commits] [libcxx] [libc++] Complete `<charconv>` for 64-bit `long double` platforms (PR #117125)

A. Jiang via libcxx-commits libcxx-commits at lists.llvm.org
Wed Nov 20 23:50:27 PST 2024


https://github.com/frederick-vs-ja created https://github.com/llvm/llvm-project/pull/117125

Towards #99940 and #99952. Implementation of P0067R5 + P0682R1 becomes complete for these platforms.

>From f5c3933d1ccba5bf3f8c1bf9f6bfd44690eda5e6 Mon Sep 17 00:00:00 2001
From: "A. Jiang" <de34 at live.cn>
Date: Thu, 21 Nov 2024 15:47:49 +0800
Subject: [PATCH] [libc++] Complete `<charconv>` for 64-bit `long double`
 platforms

---
 libcxx/docs/FeatureTestMacroTable.rst         |  4 ++--
 libcxx/docs/ReleaseNotes/20.rst               |  4 ++++
 libcxx/docs/Status/Cxx17Papers.csv            |  4 ++--
 .../__charconv/from_chars_floating_point.h    | 12 ++++++++++
 libcxx/include/__config                       |  6 +++++
 libcxx/include/version                        |  8 +++++--
 .../charconv.version.compile.pass.cpp         | 24 +++++++++----------
 .../version.version.compile.pass.cpp          | 24 +++++++++----------
 .../floating_point.pass.cpp                   | 22 +++++++++++++++++
 libcxx/test/support/charconv_test_helpers.h   |  6 ++++-
 .../generate_feature_test_macro_components.py |  7 ++++--
 11 files changed, 88 insertions(+), 33 deletions(-)

diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 3c4a13332661ee..597d0ddf3ff299 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -152,7 +152,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_string_view``                                  ``201606L``
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_to_chars``                                     *unimplemented*
+    ``__cpp_lib_to_chars``                                     ``201611L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_transparent_operators``                        ``201510L``
     ---------------------------------------------------------- -----------------
@@ -490,7 +490,7 @@ Status
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_text_encoding``                                *unimplemented*
     ---------------------------------------------------------- -----------------
-    ``__cpp_lib_to_chars``                                     *unimplemented*
+    ``__cpp_lib_to_chars``                                     ``202306L``
     ---------------------------------------------------------- -----------------
     ``__cpp_lib_to_string``                                    *unimplemented*
     ---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/20.rst b/libcxx/docs/ReleaseNotes/20.rst
index 9039c6f046445b..89c803d3b836bf 100644
--- a/libcxx/docs/ReleaseNotes/20.rst
+++ b/libcxx/docs/ReleaseNotes/20.rst
@@ -38,7 +38,11 @@ What's New in Libc++ 20.0.0?
 Implemented Papers
 ------------------
 
+- P0067R5: Elementary string conversions is implemented for platforms
+  where ``long double`` has the same format as ``double`` (`Github <https://github.com/llvm/llvm-project/issues/99940>`__)
 - P0619R4: Reviewing Deprecated Facilities of C++17 for C++20 (`Github <https://github.com/llvm/llvm-project/issues/99985>`__)
+- P0682R1: Repairing elementary string conversions is implemented for platforms
+  where ``long double`` has the same format as ``double`` (`Github <https://github.com/llvm/llvm-project/issues/99952>`__)
 - P2747R2: ``constexpr`` placement new (`Github <https://github.com/llvm/llvm-project/issues/105427>`__)
 - P2609R3: Relaxing Ranges Just A Smidge (`Github <https://github.com/llvm/llvm-project/issues/105253>`__)
 - P2985R0: A type trait for detecting virtual base classes (`Github <https://github.com/llvm/llvm-project/issues/105432>`__)
diff --git a/libcxx/docs/Status/Cxx17Papers.csv b/libcxx/docs/Status/Cxx17Papers.csv
index a589207085d36f..69f6ccc1db9844 100644
--- a/libcxx/docs/Status/Cxx17Papers.csv
+++ b/libcxx/docs/Status/Cxx17Papers.csv
@@ -71,7 +71,7 @@
 "`P0394R4 <https://wg21.link/P0394R4>`__","Hotel Parallelifornia: terminate() for Parallel Algorithms Exception Handling","2016-06 (Oulu)","|Complete|","17",""
 "","","","","",""
 "`P0003R5 <https://wg21.link/P0003R5>`__","Removing Deprecated Exception Specifications from C++17","2016-11 (Issaquah)","|Complete|","5",""
-"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","For integer types, ``std::(to|from)_chars`` has been available since v7; for ``float`` and ``double``, ``std::to_chars`` since v14 and ``std::from_chars`` since v20. Support is complete except for ``long double``."
+"`P0067R5 <https://wg21.link/P0067R5>`__","Elementary string conversions, revision 5","2016-11 (Issaquah)","|Partial|","","For integer types, ``std::(to|from)_chars`` has been available since v7; for ``float`` and ``double``, ``std::to_chars`` since v14 and ``std::from_chars`` since v20. Support is complete except for ``long double`` on platforms where its format is different ``double``."
 "`P0403R1 <https://wg21.link/P0403R1>`__","Literal suffixes for ``basic_string_view``\ ","2016-11 (Issaquah)","|Complete|","4",""
 "`P0414R2 <https://wg21.link/P0414R2>`__","Merging shared_ptr changes from Library Fundamentals to C++17","2016-11 (Issaquah)","|Complete|","11",""
 "`P0418R2 <https://wg21.link/P0418R2>`__","Fail or succeed: there is no atomic lattice","2016-11 (Issaquah)","","",""
@@ -109,5 +109,5 @@
 "`P0618R0 <https://wg21.link/P0618R0>`__","Deprecating <codecvt>","2017-02 (Kona)","|Complete|","15",""
 "`P0623R0 <https://wg21.link/P0623R0>`__","Final C++17 Parallel Algorithms Fixes","2017-02 (Kona)","|Nothing To Do|","",""
 "","","","","",""
-"`P0682R1 <https://wg21.link/P0682R1>`__","Repairing elementary string conversions","2017-07 (Toronto)","","",""
+"`P0682R1 <https://wg21.link/P0682R1>`__","Repairing elementary string conversions","2017-07 (Toronto)","|Partial|","","Support is complete in v20 on platforms where ``long double`` has the same format as ``double``."
 "`P0739R0 <https://wg21.link/P0739R0>`__","Some improvements to class template argument deduction integration into the standard library","2017-07 (Toronto)","|Complete|","5",""
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index 811e518a81db70..62152a392f51b5 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -64,6 +64,18 @@ from_chars(const char* __first, const char* __last, double& __value, chars_forma
   return std::__from_chars<double>(__first, __last, __value, __fmt);
 }
 
+#  if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_HIDE_FROM_ABI inline from_chars_result
+from_chars(const char* __first, const char* __last, long double& __value, chars_format __fmt = chars_format::general) {
+  double __dval;
+  const auto __result = std::__from_chars<double>(__first, __last, __dval, __fmt);
+  if (__result.ec == errc{})
+    __value = __dval;
+  return __result;
+}
+// TODO: Complete the implementation for platforms where long double has a different format from double.
+#  endif
+
 #endif // _LIBCPP_STD_VER >= 17
 
 _LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/include/__config b/libcxx/include/__config
index 9db00cd0c9fb93..8e8759a073e741 100644
--- a/libcxx/include/__config
+++ b/libcxx/include/__config
@@ -1231,6 +1231,12 @@ typedef __char32_t char32_t;
 #    define _LIBCPP_HAS_EXPLICIT_THIS_PARAMETER 0
 #  endif
 
+#  if defined(_MSC_VER) || __SIZEOF_LONG_DOUBLE__ == __SIZEOF_DOUBLE__
+#    define _LIBCPP_LONG_DOUBLE_IS_DOUBLE 1
+#  else
+#    define _LIBCPP_LONG_DOUBLE_IS_DOUBLE 0
+#  endif
+
 #endif // __cplusplus
 
 #endif // _LIBCPP___CONFIG
diff --git a/libcxx/include/version b/libcxx/include/version
index fc57aeade9daf2..c57f391e3f5dc7 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -357,7 +357,9 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_shared_ptr_arrays                    201611L
 # define __cpp_lib_shared_ptr_weak_type                 201606L
 # define __cpp_lib_string_view                          201606L
-// # define __cpp_lib_to_chars                             201611L
+# if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
+#   define __cpp_lib_to_chars                           201611L
+# endif
 # undef  __cpp_lib_transparent_operators
 # define __cpp_lib_transparent_operators                201510L
 # define __cpp_lib_type_trait_variable_templates        201510L
@@ -572,8 +574,10 @@ __cpp_lib_void_t                                        201411L <type_traits>
 # define __cpp_lib_string_view                          202403L
 // # define __cpp_lib_submdspan                            202306L
 // # define __cpp_lib_text_encoding                        202306L
+# if _LIBCPP_LONG_DOUBLE_IS_DOUBLE
 # undef  __cpp_lib_to_chars
-// # define __cpp_lib_to_chars                             202306L
+#   define __cpp_lib_to_chars                           202306L
+# endif
 // # define __cpp_lib_to_string                            202306L
 # undef  __cpp_lib_tuple_like
 // # define __cpp_lib_tuple_like                           202311L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp
index cc38cbacd51b73..cd83a5740c5fb5 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/charconv.version.compile.pass.cpp
@@ -50,16 +50,16 @@
 #   error "__cpp_lib_constexpr_charconv should not be defined before c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++17"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++17"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -69,16 +69,16 @@
 #   error "__cpp_lib_constexpr_charconv should not be defined before c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++20"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++20"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -91,16 +91,16 @@
 #   error "__cpp_lib_constexpr_charconv should have the value 202207L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++23"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++23"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -113,16 +113,16 @@
 #   error "__cpp_lib_constexpr_charconv should have the value 202207L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++26"
 #   endif
 #   if __cpp_lib_to_chars != 202306L
 #     error "__cpp_lib_to_chars should have the value 202306L in c++26"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index b0f8b2f80067d5..215a9ecd4a83e9 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -3081,16 +3081,16 @@
 #   error "__cpp_lib_to_array should not be defined before c++20"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++17"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++17"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -4471,16 +4471,16 @@
 #   error "__cpp_lib_to_array should have the value 201907L in c++20"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++20"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++20"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -6077,16 +6077,16 @@
 #   error "__cpp_lib_to_array should have the value 201907L in c++23"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++23"
 #   endif
 #   if __cpp_lib_to_chars != 201611L
 #     error "__cpp_lib_to_chars should have the value 201611L in c++23"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
@@ -8007,16 +8007,16 @@
 #   error "__cpp_lib_to_array should have the value 201907L in c++26"
 # endif
 
-# if !defined(_LIBCPP_VERSION)
+# if defined(TEST_LONG_DOUBLE_IS_DOUBLE)
 #   ifndef __cpp_lib_to_chars
 #     error "__cpp_lib_to_chars should be defined in c++26"
 #   endif
 #   if __cpp_lib_to_chars != 202306L
 #     error "__cpp_lib_to_chars should have the value 202306L in c++26"
 #   endif
-# else // _LIBCPP_VERSION
+# else
 #   ifdef __cpp_lib_to_chars
-#     error "__cpp_lib_to_chars should not be defined because it is unimplemented in libc++!"
+#     error "__cpp_lib_to_chars should not be defined when the requirement 'defined(TEST_LONG_DOUBLE_IS_DOUBLE)' is not met!"
 #   endif
 # endif
 
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
index 6faf0499c4c9bb..95b04214aa8824 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/floating_point.pass.cpp
@@ -15,6 +15,9 @@
 //
 // from_chars_result from_chars(const char* first, const char* last,
 //                              double& value, chars_format fmt = chars_format::general)
+//
+// from_chars_result from_chars(const char* first, const char* last,
+//                              long double& value, chars_format fmt = chars_format::general)
 
 #include <array>
 #include <charconv>
@@ -1518,6 +1521,25 @@ struct test_hex {
 //   test/std/utilities/charconv/charconv.msvc/test.cpp
 // uses random values. This tests contains errors found by this test.
 void test_random_errors() {
+#ifdef TEST_LONG_DOUBLE_IS_DOUBLE
+  {
+    const char* s    = "4.219902180869891e-2788";
+    const char* last = s + std::strlen(s) - 1;
+
+    // last + 1 contains a digit. When that value is parsed the exponent is
+    // e-2788 which returns std::errc::result_out_of_range and the value 0.
+    // the proper exponent is e-278, which can be represented by a
+    // long double whose format is same as double.
+
+    long double value             = 0.25L;
+    std::from_chars_result result = std::from_chars(s, last, value);
+
+    assert(result.ec == std::errc{});
+    assert(result.ptr == last);
+    assert(value == 4.219902180869891e-278L);
+  }
+  // TODO: Add more precise cases when the implementation for long double is complete.
+#endif
   {
     const char* s    = "4.219902180869891e-2788";
     const char* last = s + std::strlen(s) - 1;
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index fcae09478457b6..0ae7a66f421730 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -317,7 +317,11 @@ auto all_unsigned = type_list<
     >();
 auto integrals = concat(all_signed, all_unsigned);
 
-auto all_floats = type_list< float, double >(); //TODO: Add long double
+#ifdef TEST_LONG_DOUBLE_IS_DOUBLE // TODO: Remove this condition when the implementation for long double is complete.
+auto all_floats = type_list< float, double, long double >();
+#else
+auto all_floats = type_list< float, double >();
+#endif
 
 template <template <typename> class Fn, typename... Ts>
 TEST_CONSTEXPR_CXX23 void
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index 53fd44291b216a..b66ffd9723e72b 100755
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -1333,7 +1333,8 @@ def add_version_header(tc):
                 "c++26": 202306,  # P2497R0 Testing for success or failure of <charconv> functions
             },
             "headers": ["charconv"],
-            "unimplemented": True,
+            "test_suite_guard": "defined(TEST_LONG_DOUBLE_IS_DOUBLE)",
+            "libcxx_guard": "_LIBCPP_LONG_DOUBLE_IS_DOUBLE",
         },
         {
             "name": "__cpp_lib_to_string",
@@ -1532,7 +1533,9 @@ def produce_macros_definition_for_std(std):
             result += "# if %s\n" % tc["libcxx_guard"]
             inner_indent += 2
         if get_value_before(tc["values"], std) is not None:
-            assert "test_suite_guard" not in tc.keys()
+            # TRANSITION, __cpp_lib_to_chars has different values
+            # but needs to be guarded.
+            # assert "test_suite_guard" not in tc.keys()
             result += "# undef  %s\n" % tc["name"]
         line = "#%sdefine %s" % ((" " * inner_indent), tc["name"])
         line += " " * (indent - len(line))



More information about the libcxx-commits mailing list