public inbox for libstdc++@gcc.gnu.org
 help / color / mirror / Atom feed
From: Patrick Palka <ppalka@redhat.com>
To: Jonathan Wakely <jwakely@redhat.com>
Cc: libstdc++@gcc.gnu.org, gcc-patches@gcc.gnu.org, ppalka@redhat.com
Subject: Re: [PATCH] libstdc++: Ensure that std::from_chars is declared when supported
Date: Mon, 14 Mar 2022 10:15:51 -0400 (EDT)	[thread overview]
Message-ID: <d3a7cc04-b15a-0829-f2df-7935a2513818@idea> (raw)
In-Reply-To: <20220311183244.1861699-1-jwakely@redhat.com>

On Fri, 11 Mar 2022, Jonathan Wakely wrote:

> Patrick, I think this is right, but please take a look to double check.
> 
> I think we should fix the feature-test macro conditions for gcc-11 too,
> although it's a bit more complicated there. It should depend on IEEE
> float and double *and* uselocale. We don't need the other changes on the
> branch.

Don't we still depend on uselocale in GCC 12 for long double from_chars,
at least on targets where long double != binary64?

> 
> 
> -- >8 --
> 
> 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.
> 
> 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.
> ---
>  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

So ISTM we need to also check

  _GLIBCXX_HAVE_USELOCALE || (__LDBL_MANT_DIG__ == __DBL_MANT_DIG__)

or something like that :/

>  # 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);

I think __floating_from_chars_hex should work fine on 16 bit targets,
so I suppose we could use it in the !USE_LIB_FAST_FLOAT branch as well.

>  #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
>  
> -- 
> 2.34.1
> 
> 


  reply	other threads:[~2022-03-14 14:15 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-03-11 18:32 Jonathan Wakely
2022-03-14 14:15 ` Patrick Palka [this message]
2022-03-14 21:15   ` Jonathan Wakely
2022-03-15 14:12     ` Patrick Palka
2022-03-16 16:26       ` Jonathan Wakely
2022-03-16 16:54   ` Jonathan Wakely

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=d3a7cc04-b15a-0829-f2df-7935a2513818@idea \
    --to=ppalka@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jwakely@redhat.com \
    --cc=libstdc++@gcc.gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).