[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