[gcc r12-7676] libstdc++: Ensure that std::from_chars is declared when supported

Jonathan Wakely redi@gcc.gnu.org
Wed Mar 16 16:16:01 GMT 2022


https://gcc.gnu.org/g:5a4e208022e7047af3a15a3dedb715ad801db160

commit r12-7676-g5a4e208022e7047af3a15a3dedb715ad801db160
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Fri Mar 11 14:36:18 2022 +0000

    libstdc++: Ensure that std::from_chars is declared when supported
    
    This adjusts the declarations in <charconv> to match when the definition
    is present. This solves the issue that std::from_chars is present on
    Solaris 11.3 (using fast_float) but was not declared in the header
    (because the declarations were guarded by _GLIBCXX_HAVE_USELOCALE).
    
    Additionally, do not define __cpp_lib_to_chars unless both from_chars
    and to_chars are supported (which is only true for IEEE float and
    double). We might still provide from_chars (via strtold) but if to_chars
    isn't provided, we shouldn't define the feature test macro.
    
    Finally, this simplifies some of the preprocessor checks in the bodies
    of std::from_chars in src/c++17/floating_from_chars.cc and hoists the
    repeated code for the strtod version into a new function template.
    
    N.B. the long double overload of std::from_chars will always be defined
    if the float and double overloads are defined. We can always use one of
    strtold or fast_float's binary64 routines (although the latter might
    produce errors for some long double values if they are not representable
    as binary64).
    
    libstdc++-v3/ChangeLog:
    
            * include/std/charconv (__cpp_lib_to_chars): Only define when
            both from_chars and to_chars are supported for floating-point
            types.
            (from_chars, to_chars): Adjust preprocessor conditions guarding
            declarations.
            * include/std/version (__cpp_lib_to_chars): Adjust condition to
            match <charconv> definition.
            * src/c++17/floating_from_chars.cc (from_chars_strtod): New
            function template.
            (from_chars): Simplify preprocessor checks and use
            from_chars_strtod when appropriate.

Diff:
---
 libstdc++-v3/include/std/charconv             |   8 +-
 libstdc++-v3/include/std/version              |   3 +-
 libstdc++-v3/src/c++17/floating_from_chars.cc | 120 +++++++++-----------------
 3 files changed, 45 insertions(+), 86 deletions(-)

diff --git a/libstdc++-v3/include/std/charconv b/libstdc++-v3/include/std/charconv
index a3f8c7718b2..2ce9c7d4cb9 100644
--- a/libstdc++-v3/include/std/charconv
+++ b/libstdc++-v3/include/std/charconv
@@ -43,7 +43,8 @@
 #include <bits/error_constants.h> // for std::errc
 #include <ext/numeric_traits.h>
 
-#if _GLIBCXX_HAVE_USELOCALE
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
+    && __SIZE_WIDTH__ >= 32
 # define __cpp_lib_to_chars 201611L
 #endif
 
@@ -686,7 +687,7 @@ namespace __detail
   operator^=(chars_format& __lhs, chars_format __rhs) noexcept
   { return __lhs = __lhs ^ __rhs; }
 
-#if _GLIBCXX_HAVE_USELOCALE
+#if defined __cpp_lib_to_chars || _GLIBCXX_HAVE_USELOCALE
   from_chars_result
   from_chars(const char* __first, const char* __last, float& __value,
 	     chars_format __fmt = chars_format::general) noexcept;
@@ -700,8 +701,7 @@ namespace __detail
 	     chars_format __fmt = chars_format::general) noexcept;
 #endif
 
-#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
-    && __SIZE_WIDTH__ >= 32
+#if defined __cpp_lib_to_chars
   // Floating-point std::to_chars
 
   // Overloads for float.
diff --git a/libstdc++-v3/include/std/version b/libstdc++-v3/include/std/version
index 461e65b5fab..d730a7ea3c7 100644
--- a/libstdc++-v3/include/std/version
+++ b/libstdc++-v3/include/std/version
@@ -171,7 +171,8 @@
 #endif
 #define __cpp_lib_shared_ptr_weak_type 201606L
 #define __cpp_lib_string_view 201803L
-#if _GLIBCXX_HAVE_USELOCALE
+#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
+    && __SIZE_WIDTH__ >= 32
 # define __cpp_lib_to_chars 201611L
 #endif
 #define __cpp_lib_unordered_map_try_emplace 201411L
diff --git a/libstdc++-v3/src/c++17/floating_from_chars.cc b/libstdc++-v3/src/c++17/floating_from_chars.cc
index ba0426b3344..4aa2483bc28 100644
--- a/libstdc++-v3/src/c++17/floating_from_chars.cc
+++ b/libstdc++-v3/src/c++17/floating_from_chars.cc
@@ -65,6 +65,7 @@ extern "C" __ieee128 __strtoieee128(const char*, char**);
     && __SIZE_WIDTH__ >= 32
 # define USE_LIB_FAST_FLOAT 1
 # if __LDBL_MANT_DIG__ == __DBL_MANT_DIG__
+// No need to use strtold.
 #  undef USE_STRTOD_FOR_FROM_CHARS
 # endif
 #endif
@@ -420,6 +421,33 @@ namespace
     return true;
   }
 #endif
+
+  template<typename T>
+  from_chars_result
+  from_chars_strtod(const char* first, const char* last, T& value,
+		    chars_format fmt) noexcept
+  {
+    errc ec = errc::invalid_argument;
+#if _GLIBCXX_USE_CXX11_ABI
+    buffer_resource mr;
+    pmr::string buf(&mr);
+#else
+    string buf;
+    if (!reserve_string(buf))
+      return make_result(first, 0, {}, ec);
+#endif
+    size_t len = 0;
+    __try
+      {
+	if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
+	  len = from_chars_impl(pat, value, ec);
+      }
+    __catch (const std::bad_alloc&)
+      {
+	fmt = chars_format{};
+      }
+    return make_result(first, len, fmt, ec);
+  }
 #endif // USE_STRTOD_FOR_FROM_CHARS
 
 #if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
@@ -793,35 +821,15 @@ from_chars_result
 from_chars(const char* first, const char* last, float& value,
 	   chars_format fmt) noexcept
 {
-#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
+#if USE_LIB_FAST_FLOAT
   if (fmt == chars_format::hex)
     return __floating_from_chars_hex(first, last, value);
   else
     {
-      static_assert(USE_LIB_FAST_FLOAT);
       return fast_float::from_chars(first, last, value, fmt);
     }
 #else
-  errc ec = errc::invalid_argument;
-#if _GLIBCXX_USE_CXX11_ABI
-  buffer_resource mr;
-  pmr::string buf(&mr);
-#else
-  string buf;
-  if (!reserve_string(buf))
-    return make_result(first, 0, {}, ec);
-#endif
-  size_t len = 0;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-	len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+  return from_chars_strtod(first, last, value, fmt);
 #endif
 }
 
@@ -829,35 +837,15 @@ from_chars_result
 from_chars(const char* first, const char* last, double& value,
 	   chars_format fmt) noexcept
 {
-#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64
+#if USE_LIB_FAST_FLOAT
   if (fmt == chars_format::hex)
     return __floating_from_chars_hex(first, last, value);
   else
     {
-      static_assert(USE_LIB_FAST_FLOAT);
       return fast_float::from_chars(first, last, value, fmt);
     }
 #else
-  errc ec = errc::invalid_argument;
-#if _GLIBCXX_USE_CXX11_ABI
-  buffer_resource mr;
-  pmr::string buf(&mr);
-#else
-  string buf;
-  if (!reserve_string(buf))
-    return make_result(first, 0, {}, ec);
-#endif
-  size_t len = 0;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-	len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+  return from_chars_strtod(first, last, value, fmt);
 #endif
 }
 
@@ -865,41 +853,23 @@ from_chars_result
 from_chars(const char* first, const char* last, long double& value,
 	   chars_format fmt) noexcept
 {
-#if _GLIBCXX_FLOAT_IS_IEEE_BINARY32 && _GLIBCXX_DOUBLE_IS_IEEE_BINARY64 \
-  && ! USE_STRTOD_FOR_FROM_CHARS
+#if ! USE_STRTOD_FOR_FROM_CHARS
+  // Either long double is the same as double, or we can't use strtold.
+  // In the latter case, this might give an incorrect result (e.g. values
+  // out of range of double give an error, even if they fit in long double).
   double dbl_value;
   from_chars_result result;
   if (fmt == chars_format::hex)
     result = __floating_from_chars_hex(first, last, dbl_value);
   else
     {
-      static_assert(USE_LIB_FAST_FLOAT);
       result = fast_float::from_chars(first, last, dbl_value, fmt);
     }
   if (result.ec == errc{})
     value = dbl_value;
   return result;
 #else
-  errc ec = errc::invalid_argument;
-#if _GLIBCXX_USE_CXX11_ABI
-  buffer_resource mr;
-  pmr::string buf(&mr);
-#else
-  string buf;
-  if (!reserve_string(buf))
-    return make_result(first, 0, {}, ec);
-#endif
-  size_t len = 0;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-	len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+  return from_chars_strtod(first, last, value, fmt);
 #endif
 }
 
@@ -918,20 +888,8 @@ from_chars_result
 from_chars(const char* first, const char* last, __ieee128& value,
 	   chars_format fmt) noexcept
 {
-  buffer_resource mr;
-  pmr::string buf(&mr);
-  size_t len = 0;
-  errc ec = errc::invalid_argument;
-  __try
-    {
-      if (const char* pat = pattern(first, last, fmt, buf)) [[likely]]
-	len = from_chars_impl(pat, value, ec);
-    }
-  __catch (const std::bad_alloc&)
-    {
-      fmt = chars_format{};
-    }
-  return make_result(first, len, fmt, ec);
+  // fast_float doesn't support IEEE binary128 format, but we can use strtold.
+  return from_chars_strtod(first, last, value, fmt);
 }
 #endif


More information about the Libstdc++-cvs mailing list