[libcxx-commits] [libc] [libcxx] [llvm] [libcxx][libc] Hand in Hand PoC with from_chars (PR #91651)
Mark de Wever via libcxx-commits
libcxx-commits at lists.llvm.org
Mon Aug 5 10:08:21 PDT 2024
https://github.com/mordante updated https://github.com/llvm/llvm-project/pull/91651
>From 08de46ef7ed4d47ce7af473b5ea554d109811a9d Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 7 May 2024 16:50:24 -0700
Subject: [PATCH 1/6] [libcxx][libc] Hand in Hand PoC with from_chars
DO NOT MERGE, PROOF OF CONCEPT ONLY.
This patch aims to demonstrate the utility of sharing code between libc
and libc++ by using the libc float conversion code in the libc++
function from_chars. This patch adds from_chars for float and double
(long double is possible but was causing errors so was skipped here), as
well as a test to demonstrate that it works.
This is very much just a proof of concept, not intended to be committed
as-is. The from_chars code written is copied from the libc parsing code
and is not functionally complete, nor does it follow the correct coding
style.
---
libc/shared/str_to_float.h | 29 ++++
libcxx/include/CMakeLists.txt | 1 +
.../__charconv/from_chars_floating_point.h | 68 +++++++++
libcxx/include/charconv | 1 +
libcxx/src/CMakeLists.txt | 5 +-
libcxx/src/charconv.cpp | 17 +++
.../src/include/from_chars_floating_point.h | 135 ++++++++++++++++++
.../charconv.from.chars/float.pass.cpp | 70 +++++++++
libcxx/test/support/charconv_test_helpers.h | 2 +
9 files changed, 326 insertions(+), 2 deletions(-)
create mode 100644 libc/shared/str_to_float.h
create mode 100644 libcxx/include/__charconv/from_chars_floating_point.h
create mode 100644 libcxx/src/include/from_chars_floating_point.h
create mode 100644 libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
diff --git a/libc/shared/str_to_float.h b/libc/shared/str_to_float.h
new file mode 100644
index 0000000000000..da70db11f6b82
--- /dev/null
+++ b/libc/shared/str_to_float.h
@@ -0,0 +1,29 @@
+//===-- String to float conversion utils ------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SHARED_STR_TO_FLOAT_H
+#define LLVM_LIBC_SHARED_STR_TO_FLOAT_H
+
+#include "src/__support/str_to_float.h"
+
+namespace LIBC_NAMESPACE::shared {
+
+// WARNING: This is a proof of concept. In future the interface point for libcxx
+// won't be using libc internal classes.
+
+template <class T>
+inline internal::FloatConvertReturn<T> decimal_exp_to_float(
+ internal::ExpandedFloat<T> init_num, bool truncated,
+ internal::RoundDirection round, const char *__restrict num_start,
+ const size_t num_len = cpp::numeric_limits<size_t>::max()) {
+ return internal::decimal_exp_to_float(init_num, truncated, round, num_start,
+ num_len);
+}
+} // namespace LIBC_NAMESPACE::shared
+
+#endif // LLVM_LIBC_SHARED_STR_TO_FLOAT_H
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 32579272858a8..43f9e5071f3b0 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -235,6 +235,7 @@ set(files
__bit/rotate.h
__bit_reference
__charconv/chars_format.h
+ __charconv/from_chars_floating_point.h
__charconv/from_chars_integral.h
__charconv/from_chars_result.h
__charconv/tables.h
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
new file mode 100644
index 0000000000000..dd305ca8ea1dc
--- /dev/null
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -0,0 +1,68 @@
+// -*- C++ -*-
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
+#define _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
+
+#include <__assert>
+#include <__charconv/chars_format.h>
+#include <__charconv/from_chars_result.h>
+#include <__charconv/traits.h>
+#include <__config>
+#include <__system_error/errc.h>
+#include <__type_traits/enable_if.h>
+#include <__type_traits/integral_constant.h>
+#include <__type_traits/is_floating_point.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+_LIBCPP_PUSH_MACROS
+#include <__undef_macros>
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+#if _LIBCPP_STD_VER >= 17
+
+from_chars_result from_chars_floating_point(
+ const char* __first, const char* __last, float& value, chars_format fmt = chars_format::general);
+
+from_chars_result from_chars_floating_point(
+ const char* __first, const char* __last, double& value, chars_format fmt = chars_format::general);
+
+// template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, _Tp& __value, chars_format fmt = chars_format::general) {
+// return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general) {
+// return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+// inline from_chars_result
+// from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general) {
+// return std::from_chars_floating_point(__first, __last, __value, fmt);
+// }
+
+from_chars_result
+from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general);
+
+from_chars_result
+from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general);
+
+#endif // _LIBCPP_STD_VER >= 17
+
+_LIBCPP_END_NAMESPACE_STD
+
+_LIBCPP_POP_MACROS
+
+#endif // _LIBCPP___CHARCONV_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/include/charconv b/libcxx/include/charconv
index a2e270e9316dc..a8d5a5802d890 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -73,6 +73,7 @@ namespace std {
#if _LIBCPP_STD_VER >= 17
# include <__charconv/chars_format.h>
+# include <__charconv/from_chars_floating_point.h>
# include <__charconv/from_chars_integral.h>
# include <__charconv/from_chars_result.h>
# include <__charconv/tables.h>
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index fe9d2666fa4ca..41be5dcb2cbc4 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -31,6 +31,7 @@ set(LIBCXX_SOURCES
include/ryu/f2s.h
include/ryu/ryu.h
include/to_chars_floating_point.h
+ include/from_chars_floating_point.h
legacy_pointer_safety.cpp
memory.cpp
memory_resource.cpp
@@ -179,7 +180,7 @@ split_list(LIBCXX_LINK_FLAGS)
# Build the shared library.
if (LIBCXX_ENABLE_SHARED)
add_library(cxx_shared SHARED ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
- target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+ target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
PRIVATE ${LIBCXX_LIBRARIES})
set_target_properties(cxx_shared
@@ -272,7 +273,7 @@ set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
# Build the static library.
if (LIBCXX_ENABLE_STATIC)
add_library(cxx_static STATIC ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
- target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+ target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static
PRIVATE ${LIBCXX_LIBRARIES}
PRIVATE libcxx-abi-static)
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 4fd7a2c2c0f03..7d04de2dc4b35 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -9,6 +9,7 @@
#include <charconv>
#include <string.h>
+#include "include/from_chars_floating_point.h"
#include "include/to_chars_floating_point.h"
_LIBCPP_BEGIN_NAMESPACE_STD
@@ -74,4 +75,20 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
__first, __last, static_cast<double>(__value), __fmt, __precision);
}
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, float& value, chars_format fmt) {
+ return from_chars_floating_point<float>(__first, __last, value, fmt);
+}
+
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, double& value, chars_format fmt) {
+ return from_chars_floating_point<double>(__first, __last, value, fmt);
+}
+
+from_chars_result from_chars(const char* __first, const char* __last, float& __value, chars_format fmt) {
+ return std::from_chars_floating_point(__first, __last, __value, fmt);
+}
+
+from_chars_result from_chars(const char* __first, const char* __last, double& __value, chars_format fmt) {
+ return std::from_chars_floating_point(__first, __last, __value, fmt);
+}
+
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
new file mode 100644
index 0000000000000..2efb3bcb4d780
--- /dev/null
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -0,0 +1,135 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
+#define _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
+
+// NEVER DO THIS FOR REAL, this is just for demonstration purposes.
+#define LIBC_NAMESPACE libc_namespace_in_libcxx
+
+// This header is in the shared LLVM-libc header library.
+#include "shared/str_to_float.h"
+
+#include <__assert>
+#include <__config>
+#include <charconv>
+#include <limits>
+#include <type_traits>
+
+// Included for the _Floating_type_traits class
+#include "to_chars_floating_point.h"
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& value, chars_format fmt) {
+ using _Traits = _Floating_type_traits<_Tp>;
+ using _Uint_type = typename _Traits::_Uint_type;
+ ptrdiff_t length = __last - __first;
+ _LIBCPP_ASSERT_INTERNAL(length > 0, "");
+
+ // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
+ // chars_format::general case. Also, no sign, inf, or nan handling.
+ _LIBCPP_ASSERT_INTERNAL(fmt == std::chars_format::general, "");
+
+ const char* src = __first; // rename to match the libc code copied for this section.
+
+ _Uint_type mantissa = 0;
+ int exponent = 0;
+ bool truncated = false;
+ bool seen_digit = false;
+ bool after_decimal = false;
+ size_t index = 0;
+ const size_t BASE = 10;
+ constexpr char EXPONENT_MARKER = 'e';
+ constexpr char DECIMAL_POINT = '.';
+
+ // The loop fills the mantissa with as many digits as it can hold
+ const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE;
+ while (index < length) {
+ if (LIBC_NAMESPACE::internal::isdigit(src[index])) {
+ uint32_t digit = src[index] - '0';
+ seen_digit = true;
+
+ if (mantissa < bitstype_max_div_by_base) {
+ mantissa = (mantissa * BASE) + digit;
+ if (after_decimal) {
+ --exponent;
+ }
+ } else {
+ if (digit > 0)
+ truncated = true;
+ if (!after_decimal)
+ ++exponent;
+ }
+
+ ++index;
+ continue;
+ }
+ if (src[index] == DECIMAL_POINT) {
+ if (after_decimal) {
+ break; // this means that src[index] points to a second decimal point, ending the number.
+ }
+ after_decimal = true;
+ ++index;
+ continue;
+ }
+ // The character is neither a digit nor a decimal point.
+ break;
+ }
+
+ if (!seen_digit)
+ return {src + index, {}};
+
+ if (index < length && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
+ bool has_sign = false;
+ if (index + 1 < length && (src[index + 1] == '+' || src[index + 1] == '-')) {
+ has_sign = true;
+ }
+ if (index + 1 + static_cast<size_t>(has_sign) < length &&
+ LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
+ ++index;
+ auto result = LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10);
+ // if (result.has_error())
+ // output.error = result.error;
+ int32_t add_to_exponent = result.value;
+ index += result.parsed_len;
+
+ // Here we do this operation as int64 to avoid overflow.
+ int64_t temp_exponent = static_cast<int64_t>(exponent) + static_cast<int64_t>(add_to_exponent);
+
+ // If the result is in the valid range, then we use it. The valid range is
+ // also within the int32 range, so this prevents overflow issues.
+ if (temp_exponent > LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
+ exponent = LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
+ } else if (temp_exponent < -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT) {
+ exponent = -LIBC_NAMESPACE::fputil::FPBits<_Tp>::MAX_BIASED_EXPONENT;
+ } else {
+ exponent = static_cast<int32_t>(temp_exponent);
+ }
+ }
+ }
+
+ LIBC_NAMESPACE::internal::ExpandedFloat<_Tp> expanded_float = {0, 0};
+ if (mantissa != 0) {
+ auto temp = LIBC_NAMESPACE::shared::decimal_exp_to_float<_Tp>(
+ {mantissa, exponent}, truncated, LIBC_NAMESPACE::internal::RoundDirection::Nearest, src, length);
+ expanded_float = temp.num;
+ // Note: there's also an error value in temp.error. I'm not doing that error handling right now though.
+ }
+
+ auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
+ result.set_mantissa(expanded_float.mantissa);
+ result.set_biased_exponent(expanded_float.exponent);
+ value = result.get_val();
+ return {src + index, {}};
+}
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif //_LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
new file mode 100644
index 0000000000000..3419fc478fec2
--- /dev/null
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -0,0 +1,70 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11, c++14
+
+// <charconv>
+
+// constexpr from_chars_result from_chars(const char* first, const char* last,
+// Float& value, chars_format fmt = chars_format::general)
+
+#include <charconv>
+#include "test_macros.h"
+#include "charconv_test_helpers.h"
+
+template <typename T>
+struct test_basics {
+ TEST_CONSTEXPR_CXX23 void operator()() {
+ std::from_chars_result r;
+ T x;
+
+ {
+ char s[] = "001x";
+
+ // the expected form of the subject sequence is a nonempty sequence of
+ // decimal digits optionally containing a decimal-point character, then
+ // an optional exponent part as defined in 6.4.4.3, excluding any digit
+ // separators (6.4.4.2); (C23 7.24.1.5)
+ r = std::from_chars(s, s + sizeof(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 3);
+ assert(x == T(1.0));
+ }
+
+ {
+ char s[] = "1.5e10";
+
+ r = std::from_chars(s, s + sizeof(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == T(1.5e10));
+ }
+
+ {
+ char s[] = "20040229";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + sizeof(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 8);
+ assert(x == T(20040229));
+ }
+ }
+};
+
+TEST_CONSTEXPR_CXX23 bool test() {
+ run<test_basics>(all_floats);
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+
+ return 0;
+}
diff --git a/libcxx/test/support/charconv_test_helpers.h b/libcxx/test/support/charconv_test_helpers.h
index f5fbedbeb0dcd..fcae09478457b 100644
--- a/libcxx/test/support/charconv_test_helpers.h
+++ b/libcxx/test/support/charconv_test_helpers.h
@@ -317,6 +317,8 @@ auto all_unsigned = type_list<
>();
auto integrals = concat(all_signed, all_unsigned);
+auto all_floats = type_list< float, double >(); //TODO: Add long double
+
template <template <typename> class Fn, typename... Ts>
TEST_CONSTEXPR_CXX23 void
run(type_list<Ts...>)
>From b5f244027be4d7f19d26e3f6901ce30b79084310 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Thu, 9 May 2024 16:04:43 -0700
Subject: [PATCH 2/6] fix warnings and add to modulemap
---
libcxx/include/module.modulemap | 19 ++++++++++---------
.../src/include/from_chars_floating_point.h | 8 ++++----
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap
index 13d0dce34d97e..6c0ee59e2cc12 100644
--- a/libcxx/include/module.modulemap
+++ b/libcxx/include/module.modulemap
@@ -1075,18 +1075,19 @@ module std_private_bit_invert_if [system] { header "__bit/invert_if.h" }
module std_private_bit_popcount [system] { header "__bit/popcount.h" }
module std_private_bit_rotate [system] { header "__bit/rotate.h" }
-module std_private_charconv_chars_format [system] { header "__charconv/chars_format.h" }
-module std_private_charconv_from_chars_integral [system] { header "__charconv/from_chars_integral.h" }
-module std_private_charconv_from_chars_result [system] { header "__charconv/from_chars_result.h" }
-module std_private_charconv_tables [system] { header "__charconv/tables.h" }
-module std_private_charconv_to_chars [system] { header "__charconv/to_chars.h" }
-module std_private_charconv_to_chars_base_10 [system] { header "__charconv/to_chars_base_10.h" }
-module std_private_charconv_to_chars_floating_point [system] { header "__charconv/to_chars_floating_point.h" }
-module std_private_charconv_to_chars_integral [system] {
+module std_private_charconv_chars_format [system] { header "__charconv/chars_format.h" }
+module std_private_charconv_from_chars_integral [system] { header "__charconv/from_chars_integral.h" }
+module std_private_charconv_from_chars_result [system] { header "__charconv/from_chars_result.h" }
+module std_private_charconv_tables [system] { header "__charconv/tables.h" }
+module std_private_charconv_to_chars [system] { header "__charconv/to_chars.h" }
+module std_private_charconv_to_chars_base_10 [system] { header "__charconv/to_chars_base_10.h" }
+module std_private_charconv_to_chars_floating_point [system] { header "__charconv/to_chars_floating_point.h" }
+module std_private_charconv_from_chars_floating_point [system] { header "__charconv/from_chars_floating_point.h" }
+module std_private_charconv_to_chars_integral [system] {
header "__charconv/to_chars_integral.h"
export std_private_charconv_traits
}
-module std_private_charconv_to_chars_result [system] {
+module std_private_charconv_to_chars_result [system] {
header "__charconv/to_chars_result.h"
export *
}
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index 2efb3bcb4d780..2a9f4bc84eb8c 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -51,7 +51,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
// The loop fills the mantissa with as many digits as it can hold
const _Uint_type bitstype_max_div_by_base = numeric_limits<_Uint_type>::max() / BASE;
- while (index < length) {
+ while (index < static_cast<size_t>(length)) {
if (LIBC_NAMESPACE::internal::isdigit(src[index])) {
uint32_t digit = src[index] - '0';
seen_digit = true;
@@ -86,12 +86,12 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
if (!seen_digit)
return {src + index, {}};
- if (index < length && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
+ if (index < static_cast<size_t>(length) && LIBC_NAMESPACE::internal::tolower(src[index]) == EXPONENT_MARKER) {
bool has_sign = false;
- if (index + 1 < length && (src[index + 1] == '+' || src[index + 1] == '-')) {
+ if (index + 1 < static_cast<size_t>(length) && (src[index + 1] == '+' || src[index + 1] == '-')) {
has_sign = true;
}
- if (index + 1 + static_cast<size_t>(has_sign) < length &&
+ if (index + 1 + static_cast<size_t>(has_sign) < static_cast<size_t>(length) &&
LIBC_NAMESPACE::internal::isdigit(src[index + 1 + static_cast<size_t>(has_sign)])) {
++index;
auto result = LIBC_NAMESPACE::internal::strtointeger<int32_t>(src + index, 10);
>From 97ca620fe80a1691f33bc22033a20505b13b5161 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Fri, 24 May 2024 15:28:04 -0700
Subject: [PATCH 3/6] fix lint warnings and linkage errors
---
.../__charconv/from_chars_floating_point.h | 4 ++--
libcxx/src/charconv.cpp | 20 +++++++++++--------
.../src/include/from_chars_floating_point.h | 6 +++---
.../charconv.from.chars/float.pass.cpp | 4 ++--
4 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index dd305ca8ea1dc..d575dbbf611fe 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -32,10 +32,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 17
from_chars_result from_chars_floating_point(
- const char* __first, const char* __last, float& value, chars_format fmt = chars_format::general);
+ const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general);
from_chars_result from_chars_floating_point(
- const char* __first, const char* __last, double& value, chars_format fmt = chars_format::general);
+ const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general);
// template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
// inline from_chars_result
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 7d04de2dc4b35..978ca4cde8c80 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -75,20 +75,24 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
__first, __last, static_cast<double>(__value), __fmt, __precision);
}
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, float& value, chars_format fmt) {
- return from_chars_floating_point<float>(__first, __last, value, fmt);
+from_chars_result
+from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+ return from_chars_floating_point<float>(__first, __last, __value, __fmt);
}
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, double& value, chars_format fmt) {
- return from_chars_floating_point<double>(__first, __last, value, fmt);
+from_chars_result
+from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+ return from_chars_floating_point<double>(__first, __last, __value, __fmt);
}
-from_chars_result from_chars(const char* __first, const char* __last, float& __value, chars_format fmt) {
- return std::from_chars_floating_point(__first, __last, __value, fmt);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+ return from_chars_floating_point(__first, __last, __value, __fmt);
}
-from_chars_result from_chars(const char* __first, const char* __last, double& __value, chars_format fmt) {
- return std::from_chars_floating_point(__first, __last, __value, fmt);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+ return from_chars_floating_point(__first, __last, __value, __fmt);
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index 2a9f4bc84eb8c..fa6a2b3c5e534 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -27,7 +27,7 @@
_LIBCPP_BEGIN_NAMESPACE_STD
template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& value, chars_format fmt) {
+from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& __value, chars_format __fmt) {
using _Traits = _Floating_type_traits<_Tp>;
using _Uint_type = typename _Traits::_Uint_type;
ptrdiff_t length = __last - __first;
@@ -35,7 +35,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
// hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
// chars_format::general case. Also, no sign, inf, or nan handling.
- _LIBCPP_ASSERT_INTERNAL(fmt == std::chars_format::general, "");
+ _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
const char* src = __first; // rename to match the libc code copied for this section.
@@ -126,7 +126,7 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
result.set_mantissa(expanded_float.mantissa);
result.set_biased_exponent(expanded_float.exponent);
- value = result.get_val();
+ __value = result.get_val();
return {src + index, {}};
}
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index 3419fc478fec2..bf39f649084b0 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -19,7 +19,7 @@
template <typename T>
struct test_basics {
- TEST_CONSTEXPR_CXX23 void operator()() {
+ void operator()() {
std::from_chars_result r;
T x;
@@ -57,7 +57,7 @@ struct test_basics {
}
};
-TEST_CONSTEXPR_CXX23 bool test() {
+bool test() {
run<test_basics>(all_floats);
return true;
>From ae9736e472d8ae0a01bc1177a3e8419fc5bdd561 Mon Sep 17 00:00:00 2001
From: Michael Jones <michaelrj at google.com>
Date: Tue, 30 Jul 2024 13:17:09 -0700
Subject: [PATCH 4/6] address comments and create runtimes target
---
.../__charconv/from_chars_floating_point.h | 36 +++++++------------
libcxx/include/charconv | 6 ++++
libcxx/src/CMakeLists.txt | 10 +++---
libcxx/src/charconv.cpp | 14 ++------
.../src/include/from_chars_floating_point.h | 3 --
.../charconv.from.chars/float.pass.cpp | 12 ++-----
runtimes/cmake/Modules/FindLibcUtils.cmake | 8 +++++
7 files changed, 37 insertions(+), 52 deletions(-)
create mode 100644 runtimes/cmake/Modules/FindLibcUtils.cmake
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index d575dbbf611fe..15fe88e64f2b5 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -31,33 +31,21 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 17
-from_chars_result from_chars_floating_point(
- const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt);
-from_chars_result from_chars_floating_point(
- const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general);
+_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt);
-// template <typename _Tp, __enable_if_t<is_floating_point<_Tp>::value, int> = 0>
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, _Tp& __value, chars_format fmt = chars_format::general) {
-// return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
+_LIBCPP_HIDE_FROM_ABI inline from_chars_result
+from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt = chars_format::general) {
+ return std::__from_chars_floating_point(__first, __last, __value, __fmt);
+}
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general) {
-// return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
-
-// inline from_chars_result
-// from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general) {
-// return std::from_chars_floating_point(__first, __last, __value, fmt);
-// }
-
-from_chars_result
-from_chars(const char* __first, const char* __last, float& __value, chars_format fmt = chars_format::general);
-
-from_chars_result
-from_chars(const char* __first, const char* __last, double& __value, chars_format fmt = chars_format::general);
+_LIBCPP_HIDE_FROM_ABI inline from_chars_result
+from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt = chars_format::general) {
+ return std::__from_chars_floating_point(__first, __last, __value, __fmt);
+}
#endif // _LIBCPP_STD_VER >= 17
diff --git a/libcxx/include/charconv b/libcxx/include/charconv
index a8d5a5802d890..dfef8194a2a67 100644
--- a/libcxx/include/charconv
+++ b/libcxx/include/charconv
@@ -65,6 +65,12 @@ namespace std {
constexpr from_chars_result from_chars(const char* first, const char* last,
see below& value, int base = 10); // constexpr since C++23
+ constexpr from_chars_result from_chars(const char* first, const char* last,
+ float& value, chars_format fmt);
+
+ constexpr from_chars_result from_chars(const char* first, const char* last,
+ double& value, chars_format fmt);
+
} // namespace std
*/
diff --git a/libcxx/src/CMakeLists.txt b/libcxx/src/CMakeLists.txt
index 41be5dcb2cbc4..6183f42252d82 100644
--- a/libcxx/src/CMakeLists.txt
+++ b/libcxx/src/CMakeLists.txt
@@ -177,11 +177,13 @@ endif()
split_list(LIBCXX_COMPILE_FLAGS)
split_list(LIBCXX_LINK_FLAGS)
+include(FindLibcUtils)
+
# Build the shared library.
if (LIBCXX_ENABLE_SHARED)
add_library(cxx_shared SHARED ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
- target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
- target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared
+ target_include_directories(cxx_shared PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+ target_link_libraries(cxx_shared PUBLIC cxx-headers libcxx-libc-shared llvm-libc-shared-utilities
PRIVATE ${LIBCXX_LIBRARIES})
set_target_properties(cxx_shared
PROPERTIES
@@ -273,8 +275,8 @@ set(CMAKE_STATIC_LIBRARY_PREFIX "lib")
# Build the static library.
if (LIBCXX_ENABLE_STATIC)
add_library(cxx_static STATIC ${exclude_from_all} ${LIBCXX_SOURCES} ${LIBCXX_HEADERS})
- target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../libc) #TODO: Do this properly
- target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static
+ target_include_directories(cxx_static PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+ target_link_libraries(cxx_static PUBLIC cxx-headers libcxx-libc-static llvm-libc-shared-utilities
PRIVATE ${LIBCXX_LIBRARIES}
PRIVATE libcxx-abi-static)
set_target_properties(cxx_static
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 978ca4cde8c80..767b6cc9f28dd 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -76,23 +76,13 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
}
from_chars_result
-from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
+__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
return from_chars_floating_point<float>(__first, __last, __value, __fmt);
}
from_chars_result
-from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
+__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
return from_chars_floating_point<double>(__first, __last, __value, __fmt);
}
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
-from_chars(const char* __first, const char* __last, float& __value, chars_format __fmt) {
- return from_chars_floating_point(__first, __last, __value, __fmt);
-}
-
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
-from_chars(const char* __first, const char* __last, double& __value, chars_format __fmt) {
- return from_chars_floating_point(__first, __last, __value, __fmt);
-}
-
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index fa6a2b3c5e534..74a45d8a6fb2e 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -9,9 +9,6 @@
#ifndef _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
#define _LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
-// NEVER DO THIS FOR REAL, this is just for demonstration purposes.
-#define LIBC_NAMESPACE libc_namespace_in_libcxx
-
// This header is in the shared LLVM-libc header library.
#include "shared/str_to_float.h"
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index bf39f649084b0..2f13210702bea 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -10,8 +10,8 @@
// <charconv>
-// constexpr from_chars_result from_chars(const char* first, const char* last,
-// Float& value, chars_format fmt = chars_format::general)
+// from_chars_result from_chars(const char* first, const char* last,
+// Float& value, chars_format fmt = chars_format::general)
#include <charconv>
#include "test_macros.h"
@@ -57,14 +57,8 @@ struct test_basics {
}
};
-bool test() {
- run<test_basics>(all_floats);
-
- return true;
-}
-
int main(int, char**) {
- test();
+ run<test_basics>(all_floats);
return 0;
}
diff --git a/runtimes/cmake/Modules/FindLibcUtils.cmake b/runtimes/cmake/Modules/FindLibcUtils.cmake
new file mode 100644
index 0000000000000..a1378401629fa
--- /dev/null
+++ b/runtimes/cmake/Modules/FindLibcUtils.cmake
@@ -0,0 +1,8 @@
+add_library(llvm-libc-shared-utilities INTERFACE)
+# TODO: Reorganize the libc shared section so that it can be included without
+# adding the root "libc" directory to the include path.
+# TODO: Find a better way to solve the problem that ${CMAKE_SOURCE_DIR} is
+# rooted in the runtimes directory, which is why we need the ".."
+target_include_directories(llvm-libc-shared-utilities INTERFACE ${CMAKE_SOURCE_DIR}/../libc)
+target_compile_definitions(llvm-libc-shared-utilities INTERFACE LIBC_NAMESPACE=__llvm_libc_shared_utils)
+target_compile_features(llvm-libc-shared-utilities INTERFACE cxx_std_17)
>From 9109f32823345a190d4db0d15b92baeb8d7cf605 Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Aug 2024 16:40:26 +0200
Subject: [PATCH 5/6] Updates (part of) the ABI lists.
Other can be updated when their CI run fails.
---
...64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...werpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
...known-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist | 2 ++
7 files changed, 14 insertions(+)
diff --git a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index 917388f86811f..7e227263ae10a 100644
--- a/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/arm64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1587,6 +1587,8 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 033d9f9987fa8..e0aca153ca0a2 100644
--- a/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -572,6 +572,8 @@
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
index 332d8abeb03e3..d3c3012c443b0 100644
--- a/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/powerpc64-ibm-aix.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -572,6 +572,8 @@
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
+{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
{'import_export': 'EXP', 'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'storage_mapping_class': 'DS', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
index defe235a283c2..a9dd2a5851f7f 100644
--- a/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-apple-darwin.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1587,6 +1587,8 @@
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIxNS_22__cxx_atomic_base_implIxEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '__ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '__ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
index 6b77cda1e2866..55c69232375b0 100644
--- a/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-linux-android21.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1220,6 +1220,8 @@
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt6__ndk127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt6__ndk134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
index 3458b333dd6a9..356440b6cfd15 100644
--- a/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-freebsd.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1235,6 +1235,8 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIlNS_22__cxx_atomic_base_implIlEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
diff --git a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
index bdf90ba25c7fd..0fc3be345fa8d 100644
--- a/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
+++ b/libcxx/lib/abi/x86_64-unknown-linux-gnu.libcxxabi.v1.stable.exceptions.nonew.abilist
@@ -1233,6 +1233,8 @@
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKNS_17__cxx_atomic_implIiNS_22__cxx_atomic_base_implIiEEEE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__123__libcpp_atomic_monitorEPVKv', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__125notify_all_at_thread_exitERNS_18condition_variableENS_11unique_lockINS_5mutexEEE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RdNS_12chars_formatE', 'type': 'FUNC'}
+{'is_defined': True, 'name': '_ZNSt3__127__from_chars_floating_pointEPKcS1_RfNS_12chars_formatE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__131__arrive_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseEh', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__132__destroy_barrier_algorithm_baseEPNS_24__barrier_algorithm_baseE', 'type': 'FUNC'}
{'is_defined': True, 'name': '_ZNSt3__134__construct_barrier_algorithm_baseERl', 'type': 'FUNC'}
>From bebeb8e44d47a3a5b128a530795afb6cd7df794b Mon Sep 17 00:00:00 2001
From: Mark de Wever <koraq at xs4all.nl>
Date: Sun, 4 Aug 2024 18:02:20 +0200
Subject: [PATCH 6/6] Several improvements
- using availability macros
- handle leading whitespace
- implement inf and nan
- implement negative values
---
.../__charconv/from_chars_floating_point.h | 4 +-
libcxx/include/__configuration/availability.h | 13 +
libcxx/src/charconv.cpp | 4 +-
.../src/include/from_chars_floating_point.h | 169 ++++++-
.../charconv.from.chars/float.pass.cpp | 447 +++++++++++++++++-
5 files changed, 609 insertions(+), 28 deletions(-)
diff --git a/libcxx/include/__charconv/from_chars_floating_point.h b/libcxx/include/__charconv/from_chars_floating_point.h
index 15fe88e64f2b5..33ecf7012cfb6 100644
--- a/libcxx/include/__charconv/from_chars_floating_point.h
+++ b/libcxx/include/__charconv/from_chars_floating_point.h
@@ -31,10 +31,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER >= 17
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI from_chars_result
__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt);
-_LIBCPP_EXPORTED_FROM_ABI from_chars_result
+_LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_EXPORTED_FROM_ABI from_chars_result
__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt);
_LIBCPP_HIDE_FROM_ABI inline from_chars_result
diff --git a/libcxx/include/__configuration/availability.h b/libcxx/include/__configuration/availability.h
index ab483a07c9c13..edd9df87ebca9 100644
--- a/libcxx/include/__configuration/availability.h
+++ b/libcxx/include/__configuration/availability.h
@@ -87,6 +87,9 @@
// in all versions of the library are available.
#if defined(_LIBCPP_HAS_NO_VENDOR_AVAILABILITY_ANNOTATIONS)
+# define _LIBCPP_INTRODUCED_IN_LLVM_20 1
+# define _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE /* nothing */
+
# define _LIBCPP_INTRODUCED_IN_LLVM_19 1
# define _LIBCPP_INTRODUCED_IN_LLVM_19_ATTRIBUTE /* nothing */
@@ -132,6 +135,11 @@
// clang-format off
+// LLVM 20
+// TODO: Fill this in
+# define _LIBCPP_INTRODUCED_IN_LLVM_20 0
+# define _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE __attribute__((unavailable))
+
// LLVM 19
// TODO: Fill this in
# define _LIBCPP_INTRODUCED_IN_LLVM_19 0
@@ -375,6 +383,11 @@
#define _LIBCPP_AVAILABILITY_HAS_BAD_EXPECTED_ACCESS_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_19
#define _LIBCPP_AVAILABILITY_BAD_EXPECTED_ACCESS_KEY_FUNCTION _LIBCPP_INTRODUCED_IN_LLVM_19_ATTRIBUTE
+// This controls the availability of floating-point std::from_chars functions.
+// These overloads were added later than the integer overloads.
+#define _LIBCPP_AVAILABILITY_HAS_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20
+#define _LIBCPP_AVAILABILITY_FROM_CHARS_FLOATING_POINT _LIBCPP_INTRODUCED_IN_LLVM_20_ATTRIBUTE
+
// Define availability attributes that depend on _LIBCPP_HAS_NO_EXCEPTIONS.
// Those are defined in terms of the availability attributes above, and
// should not be vendor-specific.
diff --git a/libcxx/src/charconv.cpp b/libcxx/src/charconv.cpp
index 767b6cc9f28dd..8299df46a6292 100644
--- a/libcxx/src/charconv.cpp
+++ b/libcxx/src/charconv.cpp
@@ -77,12 +77,12 @@ to_chars_result to_chars(char* __first, char* __last, long double __value, chars
from_chars_result
__from_chars_floating_point(const char* __first, const char* __last, float& __value, chars_format __fmt) {
- return from_chars_floating_point<float>(__first, __last, __value, __fmt);
+ return std::__from_chars_floating_point<float>(__first, __last, __value, __fmt);
}
from_chars_result
__from_chars_floating_point(const char* __first, const char* __last, double& __value, chars_format __fmt) {
- return from_chars_floating_point<double>(__first, __last, __value, __fmt);
+ return std::__from_chars_floating_point<double>(__first, __last, __value, __fmt);
}
_LIBCPP_END_NAMESPACE_STD
diff --git a/libcxx/src/include/from_chars_floating_point.h b/libcxx/src/include/from_chars_floating_point.h
index 74a45d8a6fb2e..4156837405588 100644
--- a/libcxx/src/include/from_chars_floating_point.h
+++ b/libcxx/src/include/from_chars_floating_point.h
@@ -14,7 +14,9 @@
#include <__assert>
#include <__config>
+#include <cctype>
#include <charconv>
+#include <concepts>
#include <limits>
#include <type_traits>
@@ -23,18 +25,112 @@
_LIBCPP_BEGIN_NAMESPACE_STD
-template <typename _Tp, __enable_if_t<std::is_floating_point<_Tp>::value, int> = 0>
-from_chars_result from_chars_floating_point(const char* __first, const char* __last, _Tp& __value, chars_format __fmt) {
+// Parses an infinity string.
+// Valid strings are case insentitive and contain INF or INFINITY.
+//
+// - __first is the first argument to std::from_chars. When the string is invalid
+// this value is returned as ptr in the result.
+// - __last is the last argument of std::from_chars.
+// - __value is the value argument of std::from_chars,
+// - __ptr is the current position is the input string. This is points beyond
+// the initial I character.
+// - __negative whether a valid string represents -inf or +inf.
+template <floating_point _Tp>
+from_chars_result __from_chars_floating_point_inf(
+ const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+ if (__last - __ptr < 2) [[unlikely]]
+ return {__first, errc::invalid_argument};
+
+ if (std::tolower(__ptr[0]) != 'n' || std::tolower(__ptr[1]) != 'f') [[unlikely]]
+ return {__first, errc::invalid_argument};
+
+ __ptr += 2;
+
+ // At this point the result is valid and contains INF.
+ // When the remaining part contains INITY this will be consumed. Otherwise
+ // only INF is consumed. For example INFINITZ will consume INF and ignore
+ // INITZ.
+
+ if (__last - __ptr >= 5 //
+ && std::tolower(__ptr[0]) == 'i' //
+ && std::tolower(__ptr[1]) == 'n' //
+ && std::tolower(__ptr[2]) == 'i' //
+ && std::tolower(__ptr[3]) == 't' //
+ && std::tolower(__ptr[4]) == 'y')
+ __ptr += 5;
+
+ if constexpr (numeric_limits<_Tp>::has_infinity) {
+ if (__negative)
+ __value = -std::numeric_limits<_Tp>::infinity();
+ else
+ __value = std::numeric_limits<_Tp>::infinity();
+
+ return {__ptr, std::errc{}};
+ } else {
+ return {__ptr, errc::result_out_of_range};
+ }
+}
+
+// Parses an infinita string.
+// Valid strings are case insentitive and contain INF or INFINITY.
+//
+// - __first is the first argument to std::from_chars. When the string is invalid
+// this value is returned as ptr in the result.
+// - __last is the last argument of std::from_chars.
+// - __value is the value argument of std::from_chars,
+// - __ptr is the current position is the input string. This is points beyond
+// the initial N character.
+// - __negative whether a valid string represents -nan or +nan.
+template <floating_point _Tp>
+from_chars_result __from_chars_floating_point_nan(
+ const char* const __first, const char* __last, _Tp& __value, const char* __ptr, bool __negative) {
+ if (__last - __ptr < 2) [[unlikely]]
+ return {__first, errc::invalid_argument};
+
+ if (std::tolower(__ptr[0]) != 'a' || std::tolower(__ptr[1]) != 'n') [[unlikely]]
+ return {__first, errc::invalid_argument};
+
+ __ptr += 2;
+
+ // At this point the result is valid and contains NAN. When the remaining
+ // part contains ( n-char-sequence_opt ) this will be consumed. Otherwise
+ // only NAN is consumed. For example NAN(abcd will consume NAN and ignore
+ // (abcd.
+ if (__last - __ptr >= 2 && __ptr[0] == '(') {
+ size_t __offset = 1;
+ do {
+ if (__ptr[__offset] == ')') {
+ __ptr += __offset + 1;
+ break;
+ }
+ if (__ptr[__offset] != '_' && !std::isalnum(__ptr[__offset]))
+ break;
+ ++__offset;
+ } while (__ptr + __offset != __last);
+ }
+
+ if (__negative)
+ __value = -std::numeric_limits<_Tp>::quiet_NaN();
+ else
+ __value = std::numeric_limits<_Tp>::quiet_NaN();
+
+ return {__ptr, std::errc{}};
+}
+
+template <floating_point _Tp>
+from_chars_result __from_chars_floating_point_decimal(
+ const char* const __first,
+ const char* __last,
+ _Tp& __value,
+ chars_format __fmt,
+ const char* __ptr,
+ bool __negative) {
using _Traits = _Floating_type_traits<_Tp>;
using _Uint_type = typename _Traits::_Uint_type;
ptrdiff_t length = __last - __first;
_LIBCPP_ASSERT_INTERNAL(length > 0, "");
- // hacky parsing code as example. Not intended for actual use. I'm just going to handle the base 10
- // chars_format::general case. Also, no sign, inf, or nan handling.
- _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
-
- const char* src = __first; // rename to match the libc code copied for this section.
+ const char* src = __ptr; // rename to match the libc code copied for this section.
_Uint_type mantissa = 0;
int exponent = 0;
@@ -123,10 +219,67 @@ from_chars_result from_chars_floating_point(const char* __first, const char* __l
auto result = LIBC_NAMESPACE::fputil::FPBits<_Tp>();
result.set_mantissa(expanded_float.mantissa);
result.set_biased_exponent(expanded_float.exponent);
- __value = result.get_val();
+ if (__negative)
+ __value = -result.get_val();
+ else
+ __value = result.get_val();
return {src + index, {}};
}
+template <floating_point _Tp>
+from_chars_result
+__from_chars_floating_point(const char* const __first, const char* __last, _Tp& __value, chars_format __fmt) {
+ if (__first == __last) [[unlikely]]
+ return {__first, errc::invalid_argument};
+
+ const char* __ptr = __first;
+
+ // skip whitespace
+ while (std::isspace(*__ptr)) {
+ ++__ptr;
+ if (__ptr == __last) [[unlikely]]
+ return {__first, errc::invalid_argument}; // is this valid??
+ }
+
+ bool __negative = *__ptr == '-';
+ if (__negative) {
+ ++__ptr;
+ if (__ptr == __last) [[unlikely]]
+ return {__first, errc::invalid_argument};
+ }
+
+ if (!std::isdigit(*__ptr)) {
+ // TODO Evaluate the other implementations
+ // [charconv.from.chars]/6.2
+ // if fmt has chars_format::scientific set but not chars_format::fixed,
+ // the otherwise optional exponent part shall appear;
+ // Since INF/NAN do not have an exponent this value is not valid.
+ // See LWG3456
+ if (__fmt == chars_format::scientific)
+ return {__first, errc::invalid_argument};
+
+ switch (std::tolower(*__ptr)) {
+ case 'i':
+ return __from_chars_floating_point_inf(__first, __last, __value, __ptr + 1, __negative);
+ case 'n':
+ if constexpr (numeric_limits<_Tp>::has_quiet_NaN)
+ return __from_chars_floating_point_nan(__first, __last, __value, __ptr + 1, __negative);
+ [[fallthrough]];
+ default:
+ return {__first, errc::invalid_argument};
+ }
+ }
+
+#if 1
+ _LIBCPP_ASSERT_INTERNAL(__fmt == std::chars_format::general, "");
+#else
+ if (__fmt == chars_format::hex)
+ return std::__from_chars_floating_point_hex(__first, __last, __value);
+#endif
+
+ return std::__from_chars_floating_point_decimal(__first, __last, __value, __fmt, __ptr, __negative);
+}
+
_LIBCPP_END_NAMESPACE_STD
#endif //_LIBCPP_SRC_INCLUDE_FROM_CHARS_FLOATING_POINT_H
diff --git a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
index 2f13210702bea..99699c037b461 100644
--- a/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
+++ b/libcxx/test/std/utilities/charconv/charconv.from.chars/float.pass.cpp
@@ -8,57 +8,472 @@
// UNSUPPORTED: c++03, c++11, c++14
+// ADDITIONAL_COMPILE_FLAGS: -O0 -g
+
// <charconv>
// from_chars_result from_chars(const char* first, const char* last,
// Float& value, chars_format fmt = chars_format::general)
+#include <array>
#include <charconv>
-#include "test_macros.h"
+#include <cmath>
+#include <limits>
+
#include "charconv_test_helpers.h"
+#include "test_macros.h"
+
+template <class F>
+void test_infinity(std::chars_format fmt) {
+ const char* s = "-InFiNiTyXXX";
+ { // I
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 2, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // In
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 3, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // InF
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InF
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFi
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 5, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 6, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNi
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 7, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiT
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 8, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNiTy
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiTy
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+ { // InFiNiTyXXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == std::numeric_limits<F>::infinity());
+ }
+ { // -InFiNiTyXXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(value == -std::numeric_limits<F>::infinity());
+ }
+}
+
+template <class F>
+void test_nan(std::chars_format fmt) {
+ {
+ const char* s = "-NaN(1_A)XXX";
+ { // N
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 2, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // Na
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 3, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s + 1);
+ assert(value == F(0.25));
+ }
+ { // NaN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 0, s + 4, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 5, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 6, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 7, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 8, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 4);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_A)
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A)
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 9, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ { // NaN(1_A)XXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s + 1, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // -NaN(1_A)XXX
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + 12, value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 9);
+ assert(std::isnan(value));
+ assert(std::signbit(value));
+ }
+ }
+ {
+ const char* s = "NaN()";
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s + 5);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ { // validates a n-char-sequences with an invalid value
+ std::array s = {'N', 'a', 'N', '(', ' ', ')'};
+ s[4] = 'a';
+ {
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(s.data(), s.data() + s.size(), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s.data() + s.size());
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ for (auto c : "!@#$%^&*(-=+[]{}|\\;:'\",./<>?~` \t\v\r\n") {
+ F value = 0.25;
+ s[4] = c;
+ std::from_chars_result result = std::from_chars(s.data(), s.data() + s.size(), value, fmt);
+
+ assert(result.ec == std::errc{});
+ assert(result.ptr == s.data() + 3);
+ assert(std::isnan(value));
+ assert(!std::signbit(value));
+ }
+ }
+}
+
+template <class F>
+void test_fmt_independent(std::chars_format fmt) {
+ { // first == last
+ F value = 0.25;
+ std::from_chars_result result = std::from_chars(nullptr, nullptr, value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == nullptr);
+ assert(value == F(0.25));
+ }
+ { // only a sign
+ F value = 0.25;
+ const char* s = "-";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ if (fmt != std::chars_format::scientific) {
+ test_infinity<F>(fmt);
+ test_nan<F>(fmt);
+ } else {
+ { // infinity
+ F value = 0.25;
+ const char* s = "inf";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ { // nan
+ F value = 0.25;
+ const char* s = "nan";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ }
+ { // start with decimal separator
+ F value = 0.25;
+ const char* s = ".";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
-template <typename T>
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+ { // Invalid sign
+ F value = 0.25;
+ const char* s = "+0.25";
+ std::from_chars_result result = std::from_chars(s, s + std::strlen(s), value, fmt);
+
+ assert(result.ec == std::errc::invalid_argument);
+ assert(result.ptr == s);
+ assert(value == F(0.25));
+ }
+}
+
+template <class F>
struct test_basics {
+ void operator()() {
+ for (auto fmt :
+ {std::chars_format::scientific, std::chars_format::fixed, std::chars_format::hex, std::chars_format::general})
+ test_fmt_independent<F>(fmt);
+ }
+};
+
+template <class F>
+struct test_general {
void operator()() {
std::from_chars_result r;
- T x;
+ F x;
- {
- char s[] = "001x";
+ { // number followed by non-numeric valies
+ const char* s = "001x";
// the expected form of the subject sequence is a nonempty sequence of
// decimal digits optionally containing a decimal-point character, then
// an optional exponent part as defined in 6.4.4.3, excluding any digit
// separators (6.4.4.2); (C23 7.24.1.5)
- r = std::from_chars(s, s + sizeof(s), x);
+ r = std::from_chars(s, s + std::strlen(s), x);
assert(r.ec == std::errc{});
assert(r.ptr == s + 3);
- assert(x == T(1.0));
+ assert(x == F(1.0));
}
- {
- char s[] = "1.5e10";
+ { // double deciamal point
+ const char* s = "1.25.78";
+
+ // This number is halfway between two float values.
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
- r = std::from_chars(s, s + sizeof(s), x);
+ { // exponenent no sign
+ const char* s = "1.5e10";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
assert(r.ec == std::errc{});
assert(r.ptr == s + 6);
- assert(x == T(1.5e10));
+ assert(x == F(1.5e10));
}
+ { // exponenent + sign
+ const char* s = "1.5e+10";
- {
- char s[] = "20040229";
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e10));
+ }
+ { // exponenent - sign
+ const char* s = "1.5e-10";
- // This number is halfway between two float values.
- r = std::from_chars(s, s + sizeof(s), x);
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.5e-10));
+ }
+ { // exponent double sign
+ const char* s = "1.25e++12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 4);
+ assert(x == F(1.25));
+ }
+ { // double exponent
+ const char* s = "1.25e0e12";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 6);
+ assert(x == F(1.25));
+ }
+ { // This number is halfway between two float values.
+ const char* s = "20040229";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
assert(r.ec == std::errc{});
assert(r.ptr == s + 8);
- assert(x == T(20040229));
+ assert(x == F(20040229));
+ }
+ { // Shifting mantissa exponent and no exponent
+ const char* s = "123.456";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 7);
+ assert(x == F(1.23456e2));
+ }
+ { // Shifting mantissa exponent and an exponent
+ const char* s = "123.456e3";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + 9);
+ assert(x == F(1.23456e5));
+ }
+ { // Mantissa overflow
+ {
+ const char* s = "0.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0.111111111111111111111111111111111111111111));
+ }
+ {
+ const char* s = "111111111111.111111111111111111111111111111111111111111";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(111111111111.111111111111111111111111111111111111111111));
+ }
+ }
+ { // Leading whitespace
+ const char* s = " \t\v\r\n0.25";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(0.25));
+ }
+ { // Negative value
+ const char* s = "-0.25";
+
+ r = std::from_chars(s, s + std::strlen(s), x);
+ assert(r.ec == std::errc{});
+ assert(r.ptr == s + std::strlen(s));
+ assert(x == F(-0.25));
}
}
};
int main(int, char**) {
run<test_basics>(all_floats);
+ run<test_general>(all_floats);
return 0;
}
More information about the libcxx-commits
mailing list