From 3d211434b4b0104b86c2e1bd950e6aa5c2434121 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 10:20:44 +0200 Subject: [PATCH 1/7] Convert from_chars to use std::errc instead of errno --- .../detail/from_chars_integer_impl.hpp | 13 ++-- .../charconv/detail/from_chars_result.hpp | 4 +- include/boost/charconv/detail/parser.hpp | 64 ++++++++++--------- include/boost/charconv/from_chars.hpp | 15 +++-- src/from_chars.cpp | 5 +- test/from_chars.cpp | 39 +++++------ test/from_chars_float.cpp | 54 ++++++++-------- 7 files changed, 102 insertions(+), 92 deletions(-) diff --git a/include/boost/charconv/detail/from_chars_integer_impl.hpp b/include/boost/charconv/detail/from_chars_integer_impl.hpp index 29c7289..ca57d04 100644 --- a/include/boost/charconv/detail/from_chars_integer_impl.hpp +++ b/include/boost/charconv/detail/from_chars_integer_impl.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -70,7 +71,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs // Check pre-conditions if (!((first <= last) && (base >= 2 && base <= 36))) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } Unsigned_Integer unsigned_base = static_cast(base); @@ -95,7 +96,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs } else if (*next == '+') { - return {next, EINVAL}; + return {next, std::errc::invalid_argument}; } } @@ -122,7 +123,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs { if (next != last && (*next == '-' || *next == '+')) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } #ifdef BOOST_CHARCONV_HAS_INT128 @@ -156,7 +157,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs // If the only character was a sign abort now if (next == last) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } bool overflowed = false; @@ -186,7 +187,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs // If we have overflowed then we do not return the result if (overflowed) { - return {next, ERANGE}; + return {next, std::errc::result_out_of_range}; } value = static_cast(result); @@ -202,7 +203,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs } } - return {next, 0}; + return {next, std::errc()}; } #ifdef BOOST_MSVC diff --git a/include/boost/charconv/detail/from_chars_result.hpp b/include/boost/charconv/detail/from_chars_result.hpp index 9b26a2c..55ad493 100644 --- a/include/boost/charconv/detail/from_chars_result.hpp +++ b/include/boost/charconv/detail/from_chars_result.hpp @@ -5,6 +5,8 @@ #ifndef BOOST_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP #define BOOST_CHARCONV_DETAIL_FROM_CHARS_RESULT_HPP +#include + namespace boost { namespace charconv { // 22.13.3, Primitive numerical input conversion @@ -17,7 +19,7 @@ struct from_chars_result // 0 = no error // EINVAL = invalid_argument // ERANGE = result_out_of_range - int ec; + std::errc ec; friend constexpr bool operator==(const from_chars_result& lhs, const from_chars_result& rhs) noexcept { diff --git a/include/boost/charconv/detail/parser.hpp b/include/boost/charconv/detail/parser.hpp index a06d7c8..793d189 100644 --- a/include/boost/charconv/detail/parser.hpp +++ b/include/boost/charconv/detail/parser.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -29,7 +30,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, { if (first > last) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } auto next = first; @@ -43,7 +44,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } else if (*next == '+') { - return {next, EINVAL}; + return {next, std::errc::invalid_argument}; } else { @@ -74,7 +75,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, { significand = 0; exponent = 0; - return {next, 0}; + return {next, std::errc()}; } // Next we get the significand @@ -99,7 +100,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, // if fmt is chars_format::scientific the e is required if (fmt == chars_format::scientific) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } exponent = 0; @@ -116,12 +117,12 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } switch (r.ec) { - case EINVAL: - return {first, EINVAL}; - case ERANGE: - return {next, ERANGE}; + case std::errc::invalid_argument: + return {first, std::errc::invalid_argument}; + case std::errc::result_out_of_range: + return {next, std::errc::result_out_of_range}; default: - return {next, 0}; + return {next, std::errc()}; } } else if (*next == '.') @@ -148,7 +149,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, if (next == last) { - return {last, 0}; + return {last, std::errc()}; } } @@ -183,7 +184,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, { if (fmt == chars_format::scientific) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } if (dot_position != 0 || fractional) { @@ -206,12 +207,12 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, } switch (r.ec) { - case EINVAL: - return {first, EINVAL}; - case ERANGE: - return {next, ERANGE}; + case std::errc::invalid_argument: + return {first, std::errc::invalid_argument}; + case std::errc::result_out_of_range: + return {next, std::errc::result_out_of_range}; default: - return {next, 0}; + return {next, std::errc()}; } } else if (*next == exp_char || *next == capital_exp_char) @@ -219,13 +220,13 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, // Would be a number without a significand e.g. e+03 if (next == first) { - return {next, EINVAL}; + return {next, std::errc::invalid_argument}; } ++next; if (fmt == chars_format::fixed) { - return {first, EINVAL}; + return {first, std::errc::invalid_argument}; } exponent = i - 1; @@ -249,7 +250,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, from_chars_result r {}; - // If the significand is 0 from chars will return EINVAL because there is nothing in the buffer, + // If the significand is 0 from chars will return std::errc::invalid_argument because there is nothing in the buffer, // but it is a valid value. We need to continue parsing to get the correct value of ptr even // though we know we could bail now. // @@ -263,12 +264,15 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, { r = from_chars(significand_buffer, significand_buffer + offset, significand); } + switch (r.ec) { - case EINVAL: - return {first, EINVAL}; - case ERANGE: - return {next, ERANGE}; + case std::errc::invalid_argument: + return {first, std::errc::invalid_argument}; + case std::errc::result_out_of_range: + return {next, std::errc::result_out_of_range}; + default: + break; } if (round) @@ -313,7 +317,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, // If the exponent can't fit in the buffer the number is not representable if (next != last && i == exponent_buffer_size) { - return {next, ERANGE}; + return {next, std::errc::result_out_of_range}; } // If the exponent was e+00 or e-00 @@ -328,7 +332,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, exponent = extra_zeros; } - return {next, 0}; + return {next, std::errc()}; } const auto r = from_chars(exponent_buffer, exponent_buffer + i, exponent); @@ -337,10 +341,10 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, switch (r.ec) { - case EINVAL: - return {first, EINVAL}; - case ERANGE: - return {next, ERANGE}; + case std::errc::invalid_argument: + return {first, std::errc::invalid_argument}; + case std::errc::result_out_of_range: + return {next, std::errc::result_out_of_range}; default: if (fractional) { @@ -360,7 +364,7 @@ inline from_chars_result parser(const char* first, const char* last, bool& sign, { exponent += extra_zeros; } - return {next, 0}; + return {next, std::errc()}; } } diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index 6beb4b6..e93579f 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -15,6 +15,7 @@ #include #include #include +#include #include namespace boost { namespace charconv { @@ -108,7 +109,7 @@ from_chars_result from_chars_strtod(const char* first, const char* last, T& valu return_value = std::strtof(first, &str_end); if (return_value == HUGE_VALF) { - return {last, ERANGE}; + return {last, std::errc::result_out_of_range}; } } else BOOST_IF_CONSTEXPR (std::is_same::value) @@ -116,7 +117,7 @@ from_chars_result from_chars_strtod(const char* first, const char* last, T& valu return_value = std::strtod(first, &str_end); if (return_value == HUGE_VAL) { - return {last, ERANGE}; + return {last, std::errc::result_out_of_range}; } } else @@ -124,18 +125,18 @@ from_chars_result from_chars_strtod(const char* first, const char* last, T& valu return_value = std::strtold(first, &str_end); if (return_value == HUGE_VALL) { - return {last, ERANGE}; + return {last, std::errc::result_out_of_range}; } } // Since this is a fallback routine we are safe to check for 0 if (return_value == 0 && str_end == last) { - return {first, EINVAL}; + return {first, std::errc::result_out_of_range}; } value = return_value; - return {str_end, 0}; + return {str_end, std::errc()}; } template @@ -146,7 +147,7 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& std::int64_t exponent {}; auto r = boost::charconv::detail::parser(first, last, sign, significand, exponent, fmt); - if (r.ec != 0) + if (r.ec != std::errc()) { return r; } @@ -173,7 +174,7 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& { value = 1; r.ptr = last; - r.ec = 0; + r.ec = std::errc(); } else { diff --git a/src/from_chars.cpp b/src/from_chars.cpp index 1d72a02..accd062 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -43,7 +44,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first std::int64_t exponent {}; auto r = boost::charconv::detail::parser(first, last, sign, significand, exponent, fmt); - if (r.ec != 0) + if (r.ec != std::errc()) { value = 0.0L; return r; @@ -54,7 +55,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first if (!success) { value = 0.0L; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else { diff --git a/test/from_chars.cpp b/test/from_chars.cpp index 14d211e..d16bdc8 100644 --- a/test/from_chars.cpp +++ b/test/from_chars.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -21,7 +22,7 @@ void test_128bit_int() test_value = test_value << 126; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST(r1.ec == 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST(v1 == test_value); BOOST_TEST(std::numeric_limits::max() > static_cast(std::numeric_limits::max())); } @@ -42,7 +43,7 @@ template constexpr void constexpr_test() { constexpr auto results = constexpr_test_helper(); - static_assert(results.second.ec == 0, "No error"); + static_assert(results.second.ec == std::errc(), "No error"); static_assert(results.first == 42, "Value is 42"); } @@ -55,7 +56,7 @@ void base2_test() const char* buffer1 = "0101010"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1, 2); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, 42); } @@ -66,13 +67,13 @@ void base16_test() const char* buffer1 = "2a"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1, 16); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, 42); const char* buffer2 = "0"; T v2 = 1; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2, 16); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, 0); } @@ -85,18 +86,18 @@ void overflow_test() BOOST_IF_CONSTEXPR((std::numeric_limits::max)() < 1234) { - BOOST_TEST_EQ(r1.ec, ERANGE); + BOOST_TEST(r1.ec == std::errc::result_out_of_range); } else { - BOOST_TEST_EQ(r1.ec, 0) && BOOST_TEST_EQ(v1, 1234); + BOOST_TEST(r1.ec == std::errc()) && BOOST_TEST_EQ(v1, 1234); } const char* buffer2 = "123456789123456789123456789"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); // In the event of overflow v2 is to be returned unmodified - BOOST_TEST_EQ(r2.ec, ERANGE) && BOOST_TEST_EQ(v2, 0); + BOOST_TEST(r2.ec == std::errc::result_out_of_range) && BOOST_TEST_EQ(v2, 0); } template @@ -105,38 +106,38 @@ void invalid_argument_test() const char* buffer1 = ""; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, EINVAL); + BOOST_TEST(r1.ec == std::errc::invalid_argument); const char* buffer2 = "-"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, EINVAL); + BOOST_TEST(r2.ec == std::errc::invalid_argument); const char* buffer3 = "+"; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST_EQ(r3.ec, EINVAL); + BOOST_TEST(r3.ec == std::errc::invalid_argument); BOOST_IF_CONSTEXPR(std::is_unsigned::value) { const char* buffer4 = "-123"; T v4 = 0; auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(buffer4), v4); - BOOST_TEST_EQ(r4.ec, EINVAL); + BOOST_TEST(r4.ec == std::errc::invalid_argument); } - // Bases outside 2-36 inclusive return EINVAL + // Bases outside 2-36 inclusive return std::errc::invalid_argument const char* buffer5 = "23"; T v5 = 0; auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5, 1); - BOOST_TEST_EQ(r5.ec, EINVAL); + BOOST_TEST(r5.ec == std::errc::invalid_argument); auto r6 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5, 50); - BOOST_TEST_EQ(r6.ec, EINVAL); + BOOST_TEST(r6.ec == std::errc::invalid_argument); const char* buffer7 = "+12345"; T v7 = 3; auto r7 = boost::charconv::from_chars(buffer7, buffer7 + std::strlen(buffer7), v7); - BOOST_TEST_EQ(r7.ec, EINVAL); + BOOST_TEST(r7.ec == std::errc::invalid_argument); BOOST_TEST_EQ(v7, 3); } @@ -149,17 +150,17 @@ void simple_test() T v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); - BOOST_TEST_EQ( r.ec, 0 ) && BOOST_TEST_EQ(v, 34); + BOOST_TEST( r.ec == std::errc() ) && BOOST_TEST_EQ(v, 34); BOOST_TEST(r == r); - boost::charconv::from_chars_result r2 {r.ptr, 0}; + boost::charconv::from_chars_result r2 {r.ptr, std::errc()}; BOOST_TEST(r == r2); const char* buffer2 = "12"; T v2 = 0; auto r3 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer), v2); BOOST_TEST(r != r3); - BOOST_TEST_EQ(r3.ec, 0) && BOOST_TEST_EQ(v2, 12); + BOOST_TEST(r3.ec == std::errc()) && BOOST_TEST_EQ(v2, 12); } int main() diff --git a/test/from_chars_float.cpp b/test/from_chars_float.cpp index 6d501af..bdf265f 100644 --- a/test/from_chars_float.cpp +++ b/test/from_chars_float.cpp @@ -17,7 +17,7 @@ void spot_value(const std::string& buffer, T expected_value, boost::charconv::ch { T v = 0; auto r = boost::charconv::from_chars(buffer.c_str(), buffer.c_str() + std::strlen(buffer.c_str()), v, fmt); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); if (!BOOST_TEST_EQ(v, expected_value)) { std::cerr << "Test failure for: " << buffer << " got: " << v << std::endl; @@ -55,13 +55,13 @@ void simple_integer_test() const char* buffer1 = "12"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(12)); const char* buffer2 = "1200"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(1200)); } @@ -71,7 +71,7 @@ void simple_hex_integer_test() const char* buffer1 = "-2a"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(-42)); } @@ -81,31 +81,31 @@ void simple_scientific_test() const char* buffer1 = "1e1"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(1e1L)); const char* buffer2 = "123456789e10"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(123456789e10L)); const char* buffer3 = "1.23456789e+10"; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_EQ(v3, static_cast(1.23456789e+10L)); const char* buffer4 = "1234.56789e+10"; T v4 = 0; auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(buffer4), v4); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(v4, static_cast(1234.56789e+10L)); const char* buffer5 = "+1234.56789e+10"; auto v5 = static_cast(3.0L); auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5); - BOOST_TEST_EQ(r5.ec, EINVAL); + BOOST_TEST(r5.ec == std::errc::invalid_argument); BOOST_TEST_EQ(v5, static_cast(3.0L)); } @@ -115,13 +115,13 @@ void simple_hex_scientific_test() const char* buffer1 = "1.3a2bp-10"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(80427e-14L)); const char* buffer2 = "1.234p-10"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(4660e-13L)); } @@ -131,25 +131,25 @@ void dot_position_test() const char* buffer1 = "11.11111111"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(11.11111111L)); const char* buffer2 = "1111.111111"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(1111.111111L)); const char* buffer3 = "111111.1111"; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_EQ(v3, static_cast(111111.1111L)); const char* buffer4 = "1111111111."; T v4 = 0; auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(buffer4), v4); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(v4, static_cast(1111111111.L)); } @@ -159,37 +159,37 @@ void odd_strings_test() const char* buffer1 = "00000000000000000000000000000000000000000005"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(5)); const char* buffer2 = "123456789123456789123456789"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(1.23456789123456789123456789e26L)); const char* buffer3 = "100000000000000000000000e5"; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_EQ(v3, static_cast(100000000000000000000000e5L)); const char* buffer4 = "1.23456789123456789123456789123456789123456789e-5"; T v4 = 0; auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(buffer4), v4); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(v4, static_cast(1.23456789123456789123456789123456789123456789e-5L)); const char* buffer5 = "1.23456789123456789123456789123456789123456789e-00000000000000000005"; T v5 = 0; auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5); - BOOST_TEST_EQ(r5.ec, 0); + BOOST_TEST(r5.ec == std::errc()); BOOST_TEST_EQ(v5, static_cast(1.23456789123456789123456789123456789123456789e-5L)); const char* buffer6 = "E01"; T v6 = 0; auto r6 = boost::charconv::from_chars(buffer6, buffer6 + std::strlen(buffer6), v6); - BOOST_TEST_EQ(r6.ec, EINVAL); + BOOST_TEST(r6.ec == std::errc::invalid_argument); } template @@ -198,42 +198,42 @@ void zero_test() const char* buffer1 = "0e0"; T v1 = 0; auto r1 = boost::charconv::from_chars(buffer1, buffer1 + std::strlen(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(v1, static_cast(0)); BOOST_TEST(!std::signbit(v1)); const char* buffer2 = "-0e0"; T v2 = 0; auto r2 = boost::charconv::from_chars(buffer2, buffer2 + std::strlen(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(v2, static_cast(-0)); BOOST_TEST(std::signbit(v2)); const char* buffer3 = "0.0"; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_EQ(v3, static_cast(0.0)); BOOST_TEST(!std::signbit(v3)); const char* buffer4 = "-0.0"; T v4 = 0; auto r4 = boost::charconv::from_chars(buffer4, buffer4 + std::strlen(buffer4), v4); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(v4, static_cast(-0)); BOOST_TEST(std::signbit(v4)); const char* buffer5 = "0"; T v5 = 0; auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5); - BOOST_TEST_EQ(r5.ec, 0); + BOOST_TEST(r5.ec == std::errc()); BOOST_TEST_EQ(v5, static_cast(0)); BOOST_TEST(!std::signbit(v5)); const char* buffer6 = "-0"; T v6 = 0; auto r6 = boost::charconv::from_chars(buffer6, buffer6 + std::strlen(buffer6), v6); - BOOST_TEST_EQ(r6.ec, 0); + BOOST_TEST(r6.ec == std::errc()); BOOST_TEST_EQ(v6, static_cast(-0)); BOOST_TEST(std::signbit(v6)); } From f027ead7a092d137cd1cdf4c16c49c88562e1323 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 10:14:25 +0200 Subject: [PATCH 2/7] Convert to_chars to use std::errc instead of errno --- include/boost/charconv/to_chars.hpp | 48 ++++++++++------------ test/limits.cpp | 21 +++++----- test/roundtrip.cpp | 9 +++-- test/test_boost_json_values.cpp | 13 +++--- test/test_parser.cpp | 37 ++++++++--------- test/to_chars.cpp | 63 +++++++++++++++-------------- test/to_chars_float.cpp | 31 +++++++------- test/to_chars_float_STL_comp.cpp | 8 ++-- test/to_chars_sprintf.cpp | 5 ++- 9 files changed, 119 insertions(+), 116 deletions(-) diff --git a/include/boost/charconv/to_chars.hpp b/include/boost/charconv/to_chars.hpp index a357740..345c9b7 100644 --- a/include/boost/charconv/to_chars.hpp +++ b/include/boost/charconv/to_chars.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -34,12 +35,7 @@ namespace boost { namespace charconv { struct to_chars_result { char* ptr; - - // Values: - // 0 = no error - // EINVAL = invalid_argument - // ERANGE = result_out_of_range - int ec; + std::errc ec; constexpr friend bool operator==(const to_chars_result& lhs, const to_chars_result& rhs) noexcept { @@ -119,7 +115,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char if (first > last) { - return {last, EINVAL}; + return {last, std::errc::invalid_argument}; } // Strip the sign from the value and apply at the end after parsing if the type is signed @@ -155,7 +151,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char if (converted_value_digits > user_buffer_size) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } decompose32(converted_value, buffer); @@ -175,7 +171,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char if (converted_value_digits > user_buffer_size) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } if (is_negative) @@ -228,7 +224,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char } } - return {first + converted_value_digits, 0}; + return {first + converted_value_digits, std::errc()}; } #ifdef BOOST_CHARCONV_HAS_INT128 @@ -248,7 +244,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_128integer_impl(char* first, c if (first > last) { - return {last, EINVAL}; + return {last, std::errc::invalid_argument}; } // Strip the sign from the value and apply at the end after parsing if the type is signed @@ -275,7 +271,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_128integer_impl(char* first, c if (converted_value_digits > user_buffer_size) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } if (is_negative) @@ -314,7 +310,7 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_128integer_impl(char* first, c offset += 9; } - return {first + converted_value_digits, 0}; + return {first + converted_value_digits, std::errc()}; } #endif @@ -327,13 +323,13 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char if (!((first <= last) && (base >= 2 && base <= 36))) { - return {last, EINVAL}; + return {last, std::errc::invalid_argument}; } if (value == 0) { *first++ = '0'; - return {first, 0}; + return {first, std::errc()}; } Unsigned_Integer unsigned_value {}; @@ -418,12 +414,12 @@ BOOST_CHARCONV_CONSTEXPR to_chars_result to_chars_integer_impl(char* first, char if (num_chars > output_length) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } boost::charconv::detail::memcpy(first, buffer + (buffer_size - num_chars), num_chars); - return {first + num_chars, 0}; + return {first + num_chars, std::errc()}; } #ifdef BOOST_MSVC @@ -469,7 +465,7 @@ to_chars_result to_chars_hex(char* first, char* last, Real value, int precision) const std::ptrdiff_t buffer_size = last - first; if (buffer_size < real_precision || first > last) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } // Handle edge cases first @@ -481,14 +477,14 @@ to_chars_result to_chars_hex(char* first, char* last, Real value, int precision) case FP_NAN: // The dragonbox impl will return the correct type of NaN ptr = boost::charconv::detail::to_chars(value, first, chars_format::general); - return { ptr, 0 }; + return { ptr, std::errc() }; case FP_ZERO: if (std::signbit(value)) { *first++ = '-'; } std::memcpy(first, "0p+0", 4); - return {first + 4, 0}; + return {first + 4, std::errc()}; } // Extract the significand and the exponent @@ -552,7 +548,7 @@ to_chars_result to_chars_hex(char* first, char* last, Real value, int precision) const std::ptrdiff_t total_length = (value < 0) + 2 + real_precision + 2 + num_digits(abs_unbiased_exponent); if (total_length > buffer_size) { - return {last, EOVERFLOW}; + return {last, std::errc::result_out_of_range}; } // Round if required @@ -660,7 +656,7 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f } auto r = to_chars_integer_impl(first, last, value_struct.significand); - if (r.ec != 0) + if (r.ec != std::errc()) { return r; } @@ -679,7 +675,7 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f abs_value /= 10; } - return { r.ptr, 0 }; + return { r.ptr, std::errc() }; } else if (abs_value >= max_fractional_value && abs_value < max_value) { @@ -692,13 +688,13 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f else { auto* ptr = boost::charconv::detail::to_chars(value, first, fmt); - return { ptr, 0 }; + return { ptr, std::errc() }; } } else if (fmt == boost::charconv::chars_format::scientific) { auto* ptr = boost::charconv::detail::to_chars(value, first, fmt); - return { ptr, 0 }; + return { ptr, std::errc() }; } } else @@ -706,7 +702,7 @@ to_chars_result to_chars_float_impl(char* first, char* last, Real value, chars_f if (fmt != boost::charconv::chars_format::hex) { auto* ptr = boost::charconv::detail::floff(value, precision, first, fmt); - return { ptr, 0 }; + return { ptr, std::errc() }; } } diff --git a/test/limits.cpp b/test/limits.cpp index 0b1a76b..512f6a5 100644 --- a/test/limits.cpp +++ b/test/limits.cpp @@ -61,6 +61,7 @@ std::ostream& operator<<( std::ostream& os, boost::int128_type v ) #include #include #include +#include #include void test_odr_use( int const* ); @@ -71,24 +72,24 @@ template void test_integral( T value ) { char buffer[ boost::charconv::limits::max_chars10 ]; auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST(r.ec == std::errc()); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2 ); - BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ); + BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); } // base 10 { char buffer[ boost::charconv::limits::max_chars10 ]; auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value, 10 ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST(r.ec == std::errc()); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, 10 ); - BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ); + BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); } // any base @@ -96,12 +97,12 @@ template void test_integral( T value ) { char buffer[ boost::charconv::limits::max_chars ]; auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value, base ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST(r.ec == std::errc()); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, base ); - BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ); + BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); } } @@ -123,24 +124,24 @@ template void test_floating_point( T value ) { char buffer[ boost::charconv::limits::max_chars10 ]; auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST(r.ec == std::errc()); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2 ); - BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ); + BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); } // no base, max_chars { char buffer[ boost::charconv::limits::max_chars ]; auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST(r.ec == std::errc()); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2 ); - BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ); + BOOST_TEST(r2.ec == std::errc()) && BOOST_TEST_EQ( v2, value ); } } diff --git a/test/roundtrip.cpp b/test/roundtrip.cpp index caaa703..7788788 100644 --- a/test/roundtrip.cpp +++ b/test/roundtrip.cpp @@ -59,6 +59,7 @@ std::ostream& operator<<( std::ostream& os, boost::int128_type v ) #include #include #include +#include #include #include #include @@ -78,12 +79,12 @@ template void test_roundtrip( T value, int base ) auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value, base ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST( r.ec == std::errc() ); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2, base ); - if( BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ) ) + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST_EQ( v2, value ) ) { } else @@ -217,12 +218,12 @@ template void test_roundtrip( T value ) auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST( r.ec == std::errc() ); T v2 = 0; auto r2 = boost::charconv::from_chars( buffer, r.ptr, v2 ); - if( BOOST_TEST_EQ( r2.ec, 0 ) && BOOST_TEST_EQ( v2, value ) ) + if( BOOST_TEST( r2.ec == std::errc() ) && BOOST_TEST_EQ( v2, value ) ) { } else diff --git a/test/test_boost_json_values.cpp b/test/test_boost_json_values.cpp index fcaf503..a06bb7f 100644 --- a/test/test_boost_json_values.cpp +++ b/test/test_boost_json_values.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -25,7 +26,7 @@ void grind(const std::string& str, const T expected_value) // From string to expected value T v {}; auto from_r = boost::charconv::from_chars(str.c_str(), str.c_str() + std::strlen(str.c_str()), v); - if (!(BOOST_TEST_EQ(v, expected_value) && BOOST_TEST_EQ(from_r.ec, 0))) + if (!(BOOST_TEST_EQ(v, expected_value) && BOOST_TEST(from_r.ec == std::errc()))) { std::cerr << "Expected value: " << expected_value << "\nFrom chars value: " << v << std::endl; return; @@ -35,10 +36,10 @@ void grind(const std::string& str, const T expected_value) T roundtrip_v {}; char buffer[256] {}; auto to_r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), expected_value); - BOOST_TEST_EQ(to_r.ec, 0); + BOOST_TEST(to_r.ec == std::errc()); auto roundtrip_r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), roundtrip_v); - if (!(BOOST_TEST_EQ(roundtrip_v, expected_value) && BOOST_TEST_EQ(roundtrip_r.ec, 0))) + if (!(BOOST_TEST_EQ(roundtrip_v, expected_value) && BOOST_TEST(roundtrip_r.ec == std::errc()))) { std::cerr << "Expected value: " << expected_value << "\nRoundtrip value: " << roundtrip_v << std::endl; return; @@ -65,7 +66,7 @@ void spot_value(const std::string& buffer, T expected_value) { T v = 0; auto r = boost::charconv::from_chars(buffer.c_str(), buffer.c_str() + std::strlen(buffer.c_str()), v); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); if (!BOOST_TEST_EQ(v, expected_value)) { std::cerr << "Test failure for: " << buffer << " got: " << v << std::endl; @@ -130,11 +131,11 @@ void issue_599_test() { char buffer[256] {}; const auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), current_ref_val); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); double return_val {}; const auto return_r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), return_val); - BOOST_TEST_EQ(return_r.ec, 0); + BOOST_TEST(return_r.ec == std::errc()); if (!BOOST_TEST_EQ(current_ref_val, return_val)) { #ifdef BOOST_CHARCONV_DEBUG diff --git a/test/test_parser.cpp b/test/test_parser.cpp index fb3e56b..8b54df2 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -19,7 +20,7 @@ void test_integer() const char* val1 = "12"; auto r1 = boost::charconv::detail::parser(val1, val1 + std::strlen(val1), sign, significand, exponent); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(significand, 12); BOOST_TEST_EQ(exponent, 0); @@ -30,13 +31,13 @@ void test_integer() const char* val2 = "123456789"; auto r2 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, 0); BOOST_TEST_EQ(significand, 123456789); auto r3 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent, boost::charconv::chars_format::scientific); - BOOST_TEST_EQ(r3.ec, EINVAL); + BOOST_TEST(r3.ec == std::errc::invalid_argument); } template @@ -48,7 +49,7 @@ void test_scientifc() const char* val1 = "-1e1"; auto r1 = boost::charconv::detail::parser(val1, val1 + std::strlen(val1), sign, significand, exponent); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(sign, true); BOOST_TEST_EQ(significand, 1); BOOST_TEST_EQ(exponent, 1); @@ -59,7 +60,7 @@ void test_scientifc() const char* val2 = "123456789e10"; auto r2 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, 10); BOOST_TEST_EQ(significand, 123456789); @@ -70,31 +71,31 @@ void test_scientifc() const char* val3 = "1.23456789e+10"; auto r3 = boost::charconv::detail::parser(val3, val3 + std::strlen(val3), sign, significand, exponent); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, 2); BOOST_TEST_EQ(significand, 123456789); const char* val4 = "1.23456789e-10"; auto r4 = boost::charconv::detail::parser(val4, val4 + std::strlen(val4), sign, significand, exponent); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, -18); BOOST_TEST_EQ(significand, 123456789); auto r5 = boost::charconv::detail::parser(val4, val4 + std::strlen(val4), sign, significand, exponent, boost::charconv::chars_format::fixed); - BOOST_TEST_EQ(r5.ec, EINVAL); + BOOST_TEST(r5.ec == std::errc::invalid_argument); const char* val6 = "987654321e10"; auto r6 = boost::charconv::detail::parser(val6, val6 + std::strlen(val6), sign, significand, exponent); - BOOST_TEST_EQ(r6.ec, 0); + BOOST_TEST(r6.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, 10); BOOST_TEST_EQ(significand, 987654321); const char* val7 = "1.23456789E+10"; auto r7 = boost::charconv::detail::parser(val7, val7 + std::strlen(val7), sign, significand, exponent); - BOOST_TEST_EQ(r7.ec, 0); + BOOST_TEST(r7.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(exponent, 2); BOOST_TEST_EQ(significand, 123456789); @@ -110,7 +111,7 @@ void test_hex_integer() const char* val1 = "2a"; auto r1 = boost::charconv::detail::parser(val1, val1 + std::strlen(val1), sign, significand, exponent, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(significand, 42); BOOST_TEST_EQ(exponent, 0); @@ -121,13 +122,13 @@ void test_hex_integer() const char* val2 = "-1a3b5c7d9"; auto r2 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(sign, true); BOOST_TEST_EQ(exponent, 0); BOOST_TEST_EQ(significand, 7041566681); auto r3 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent, boost::charconv::chars_format::scientific); - BOOST_TEST_EQ(r3.ec, EINVAL); + BOOST_TEST(r3.ec == std::errc::invalid_argument); } template @@ -139,7 +140,7 @@ void test_hex_scientific() const char* val1 = "2ap+5"; auto r1 = boost::charconv::detail::parser(val1, val1 + std::strlen(val1), sign, significand, exponent, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_EQ(sign, false); BOOST_TEST_EQ(significand, 42); BOOST_TEST_EQ(exponent, 5); @@ -150,23 +151,23 @@ void test_hex_scientific() const char* val2 = "-1.3a2bp-10"; auto r2 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_EQ(sign, true); BOOST_TEST_EQ(exponent, -14); BOOST_TEST_EQ(significand, 80427); auto r3 = boost::charconv::detail::parser(val2, val2 + std::strlen(val2), sign, significand, exponent, boost::charconv::chars_format::scientific); - BOOST_TEST_EQ(r3.ec, EINVAL); + BOOST_TEST(r3.ec == std::errc::invalid_argument); const char* val4 = "-1.3A2BP-10"; auto r4 = boost::charconv::detail::parser(val4, val4 + std::strlen(val4), sign, significand, exponent, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_EQ(sign, true); BOOST_TEST_EQ(exponent, -14); BOOST_TEST_EQ(significand, 80427); } -int main(void) +int main() { test_integer(); test_integer(); diff --git a/test/to_chars.cpp b/test/to_chars.cpp index 4abca3b..2cc356e 100644 --- a/test/to_chars.cpp +++ b/test/to_chars.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -19,14 +20,14 @@ void test_128bit_int() char buffer1[64] {}; T v1 = static_cast(1234); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "1234"); // Use 64-bit path char buffer2[64] {}; T v2 = static_cast(1234123412341234LL); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "1234123412341234"); // Use 128-bit path @@ -35,12 +36,12 @@ void test_128bit_int() test_value = test_value << 126; T v3 = 0; auto r3 = boost::charconv::from_chars(buffer3, buffer3 + std::strlen(buffer3), v3); - BOOST_TEST(r3.ec == 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST(v3 == test_value); char buffer4[64] {}; auto r4 = boost::charconv::to_chars(buffer4, buffer4 + sizeof(buffer4), v3); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer3, buffer4); // Failing from roundtrip test @@ -50,35 +51,35 @@ void test_128bit_int() const char* buffer5 = "-103527168272318384816037687533325012784"; T v5 = 0; auto r5 = boost::charconv::from_chars(buffer5, buffer5 + std::strlen(buffer5), v5); - BOOST_TEST(r5.ec == 0); + BOOST_TEST(r5.ec == std::errc()); BOOST_TEST(v5 < 0); char buffer6[64] {}; auto r6 = boost::charconv::to_chars(buffer6, buffer6 + sizeof(buffer6), v5); - BOOST_TEST_EQ(r6.ec, 0); + BOOST_TEST(r6.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer5, buffer6); // And back again T v7 = 0; auto r7 = boost::charconv::from_chars(buffer6, buffer6 + std::strlen(buffer6), v7); - BOOST_TEST(r7.ec == 0); + BOOST_TEST(r7.ec == std::errc()); BOOST_TEST(v5 == v7);; // Second failing test const char* buffer10 = "-170141183460469231731687303715884105728"; T v10 = 0; auto r10 = boost::charconv::from_chars(buffer10, buffer10 + std::strlen(buffer10), v10); - BOOST_TEST(r10.ec == 0); + BOOST_TEST(r10.ec == std::errc()); BOOST_TEST(v10 < 0); char buffer11[64] {}; auto r11 = boost::charconv::to_chars(buffer11, buffer11 + sizeof(buffer11), v10); - BOOST_TEST_EQ(r11.ec, 0); + BOOST_TEST(r11.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer10, buffer11); T v11 = 0; auto r12 = boost::charconv::from_chars(buffer11, buffer11 + std::strlen(buffer11), v11); - BOOST_TEST(r12.ec == 0); + BOOST_TEST(r12.ec == std::errc()); BOOST_TEST(v10 == v11); } } @@ -89,7 +90,7 @@ void specific_value_tests(T value) { char buffer[64] {}; auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, value); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); std::string value_string = std::to_string(value); BOOST_TEST_CSTR_EQ(buffer, value_string.c_str()); } @@ -98,7 +99,7 @@ void off_by_one_tests(int value) { char buffer[64] {}; auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, value); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); std::string value_string = std::to_string(value); BOOST_TEST_CSTR_EQ(buffer, value_string.c_str()); } @@ -109,7 +110,7 @@ void base_thirtytwo_tests() char buffer1[64] {}; T v1 = static_cast(42); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 32); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "1a"); } @@ -119,7 +120,7 @@ void base_sixteen_tests() char buffer1[64] {}; T v1 = static_cast(42); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 16); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "2a"); } @@ -129,7 +130,7 @@ void base_eight_tests() char buffer1[64] {}; T v1 = static_cast(42); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 8); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "52"); } @@ -139,7 +140,7 @@ void base_four_tests() char buffer1[64] {}; T v1 = static_cast(42); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 4); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "222"); } @@ -150,13 +151,13 @@ void base_30_tests() char buffer1[64] {}; T v1 = static_cast(1234); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 30); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "1b4"); char buffer2[64] {}; T v2 = static_cast(-4321); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2, 30); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "-4o1"); } @@ -166,12 +167,12 @@ void overflow_tests() char buffer1[2] {}; T v1 = static_cast(250); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1); - BOOST_TEST_EQ(r1.ec, EOVERFLOW); + BOOST_TEST(r1.ec == std::errc::result_out_of_range); char buffer2[3] {}; T v2 = static_cast(12341234); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2); - BOOST_TEST_EQ(r2.ec, EOVERFLOW); + BOOST_TEST(r2.ec == std::errc::result_out_of_range); } template @@ -180,7 +181,7 @@ void base_two_tests() char buffer1[64] {}; T v1 = static_cast(42); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1, 2); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "101010"); } @@ -190,13 +191,13 @@ void sixty_four_bit_tests() char buffer1[64] {}; T v1 = static_cast(-1234); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "-1234"); char buffer2[64] {}; T v2 = static_cast(1234123412341234LL); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "1234123412341234"); } @@ -206,21 +207,21 @@ void sixty_four_bit_tests() char buffer1[64] {}; std::uint64_t v1 = (std::numeric_limits::max)(); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "18446744073709551615"); // Cutting this value in half would overflow a 32 bit unsigned for the back 10 digits char buffer2[64] {}; std::uint64_t v2 = UINT64_C(9999999999999999999); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "9999999999999999999"); // Account for zeros in the back half of the split char buffer3[64] {}; std::uint64_t v3 = UINT64_C(10000000000000000000); auto r3 = boost::charconv::to_chars(buffer3, buffer3 + sizeof(buffer3) - 1, v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer3, "10000000000000000000"); } @@ -230,7 +231,7 @@ void negative_vals_test() char buffer1[10] {}; T v = -4321; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "-4321"); } @@ -240,7 +241,7 @@ void simple_test() char buffer1[64] {}; T v = 34; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1) - 1, v); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "34"); boost::charconv::to_chars_result r {r1.ptr, r1.ec}; @@ -250,16 +251,16 @@ void simple_test() T v2 = 12; auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2) - 1, v2); BOOST_TEST(r1 != r2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "12"); // If the base is not 2-36 inclusive return invalid value char buffer3[64] {}; T v3 = 12; auto r3 = boost::charconv::to_chars(buffer3, buffer3 + sizeof(buffer3) - 1, v3, -2); - BOOST_TEST_EQ(r3.ec, EINVAL); + BOOST_TEST(r3.ec == std::errc::invalid_argument); auto r4 = boost::charconv::to_chars(buffer3, buffer3 + sizeof(buffer3) - 1, v3, 90); - BOOST_TEST_EQ(r4.ec, EINVAL); + BOOST_TEST(r4.ec == std::errc::invalid_argument); } int main() diff --git a/test/to_chars_float.cpp b/test/to_chars_float.cpp index 02698de..a3333f8 100644 --- a/test/to_chars_float.cpp +++ b/test/to_chars_float.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -21,19 +22,19 @@ void printf_divergence() char buffer1[256] {}; T v1 = 3.4; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "3.4"); char buffer2[256] {}; T v2 = 3000.40; auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2), v2); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "3000.4"); char buffer3[256] {}; T v3 = -3000000300000000.5; auto r3 = boost::charconv::to_chars(buffer3, buffer3 + sizeof(buffer3), v3); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer3, "-3000000300000000.5"); } @@ -43,11 +44,11 @@ void integer_general_format() char buffer1[256] {}; T v1 = 1217.2772861138403; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "1217.2772861138403"); T return_v1; auto r1_return = boost::charconv::from_chars(buffer1, buffer1 + strlen(buffer1), return_v1); - BOOST_TEST_EQ(r1_return.ec, 0); + BOOST_TEST(r1_return.ec == std::errc()); BOOST_TEST_EQ(return_v1, v1); } @@ -57,37 +58,37 @@ void non_finite_values(boost::charconv::chars_format fmt = boost::charconv::char char buffer1[256] {}; T v1 = std::numeric_limits::infinity(); auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1), v1, fmt, precision); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "inf"); char buffer2[256] {}; T v2 = -std::numeric_limits::infinity(); auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2), v2, fmt, precision); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "-inf"); char buffer3[256] {}; T v3 = std::numeric_limits::quiet_NaN(); auto r3 = boost::charconv::to_chars(buffer3, buffer3 + sizeof(buffer3), v3, fmt, precision); - BOOST_TEST_EQ(r3.ec, 0); + BOOST_TEST(r3.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer3, "nan"); char buffer4[256] {}; T v4 = -std::numeric_limits::quiet_NaN(); auto r4 = boost::charconv::to_chars(buffer4, buffer4 + sizeof(buffer4), v4, fmt, precision); - BOOST_TEST_EQ(r4.ec, 0); + BOOST_TEST(r4.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer4, "-nan(ind)"); char buffer5[256] {}; T v5 = std::numeric_limits::signaling_NaN(); auto r5 = boost::charconv::to_chars(buffer5, buffer5 + sizeof(buffer5), v5, fmt, precision); - BOOST_TEST_EQ(r5.ec, 0); + BOOST_TEST(r5.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer5, "nan(snan)"); char buffer6[256] {}; T v6 = -std::numeric_limits::signaling_NaN(); auto r6 = boost::charconv::to_chars(buffer6, buffer6 + sizeof(buffer6), v6, fmt, precision); - BOOST_TEST_EQ(r6.ec, 0); + BOOST_TEST(r6.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer6, "-nan(snan)"); } @@ -97,7 +98,7 @@ void fixed_values() char buffer1[256] {}; T v1 = 61851632; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1), v1); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "61851632"); } @@ -107,13 +108,13 @@ void failing_ci_values() char buffer1[256] {}; T v1 = -1.08260383390082946e+307; auto r1 = boost::charconv::to_chars(buffer1, buffer1 + sizeof(buffer1), v1, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r1.ec, 0); + BOOST_TEST(r1.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer1, "-1.ed5658af91a0fp+1019"); char buffer2[256] {}; T v2 = -9.52743282403084637e+306; auto r2 = boost::charconv::to_chars(buffer2, buffer2 + sizeof(buffer2), v2, boost::charconv::chars_format::hex); - BOOST_TEST_EQ(r2.ec, 0); + BOOST_TEST(r2.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer2, "-1.b22914956c56fp+1019"); } @@ -122,7 +123,7 @@ void spot_check(T v, const std::string& str, boost::charconv::chars_format fmt = { char buffer[256] {}; const auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer), v, fmt); - BOOST_TEST_EQ(r.ec, 0); + BOOST_TEST(r.ec == std::errc()); BOOST_TEST_CSTR_EQ(buffer, str.c_str()); } diff --git a/test/to_chars_float_STL_comp.cpp b/test/to_chars_float_STL_comp.cpp index 7a4c476..a299e30 100644 --- a/test/to_chars_float_STL_comp.cpp +++ b/test/to_chars_float_STL_comp.cpp @@ -63,7 +63,7 @@ void test_spot(T val, boost::charconv::chars_format fmt = boost::charconv::chars r_stl = std::to_chars(buffer_stl, buffer_stl + sizeof(buffer_stl), val, stl_fmt, precision); } - BOOST_TEST_EQ(r_boost.ec, 0); + BOOST_TEST(r_boost.ec == std::errc()); if (r_stl.ec != std::errc()) { // STL failed @@ -114,16 +114,16 @@ void non_finite_test(boost::charconv::chars_format fmt = boost::charconv::chars_ test_spot(-std::numeric_limits::infinity(), fmt, i); test_spot(std::numeric_limits::quiet_NaN(), fmt, i); - #if (defined(__clang__) && __clang_major__ >= 16) || defined(_MSC_VER) + #if (defined(__clang__) && __clang_major__ >= 16 && defined(__APPLE__)) || defined(_MSC_VER) // - // Newer clang and MSVC both give the following: + // Newer apple clang and MSVC both give the following: // // -qNaN = -nan(ind) // test_spot(-std::numeric_limits::quiet_NaN(), fmt, i); #endif - #if (defined(__clang__) && __clang_major__ >= 16) + #if (defined(__clang__) && __clang_major__ >= 16 && defined(__APPLE__)) // // Newer clang also gives the following: // diff --git a/test/to_chars_sprintf.cpp b/test/to_chars_sprintf.cpp index 86e6cdb..f067d04 100644 --- a/test/to_chars_sprintf.cpp +++ b/test/to_chars_sprintf.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -104,7 +105,7 @@ template void test_sprintf( T value ) auto r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value ); - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST( r.ec == std::errc() ); char buffer2[ 256 ]; std::snprintf( buffer2, sizeof( buffer2 ), fmt_from_type( value ), value ); @@ -133,7 +134,7 @@ template void test_sprintf_float( T value, boost::charconv::chars_forma r = boost::charconv::to_chars( buffer, buffer + sizeof( buffer ), value, fmt, std::numeric_limits::max_digits10); } - BOOST_TEST_EQ( r.ec, 0 ); + BOOST_TEST( r.ec == std::errc() ); char buffer2[ 256 ]; From f9b0b764999f96f8dc7f3d58f62a603da872ed3b Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 10:21:09 +0200 Subject: [PATCH 3/7] Add function to convert errno to std::errc --- include/boost/charconv/from_chars.hpp | 2 ++ src/from_chars.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index e93579f..8dda4cf 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -244,6 +244,8 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& return r; } +std::errc errno_to_errc(int errno_value) noexcept; + } // Namespace detail #ifdef BOOST_MSVC diff --git a/src/from_chars.cpp b/src/from_chars.cpp index accd062..3d967ed 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -13,6 +13,19 @@ # pragma GCC diagnostic ignored "-Wmissing-field-initializers" #endif +std::errc boost::charconv::detail::errno_to_errc(int errno_value) noexcept +{ + switch (errno_value) + { + case EINVAL: + return std::errc::invalid_argument; + case ERANGE: + return std::errc::result_out_of_range; + default: + return std::errc(); + } +} + boost::charconv::from_chars_result boost::charconv::from_chars(const char* first, const char* last, float& value, boost::charconv::chars_format fmt) noexcept { return boost::charconv::detail::from_chars_float_impl(first, last, value, fmt); From 94830e29de7431711d44f0fa83fb92b6ace3e760 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 10:56:14 +0200 Subject: [PATCH 4/7] Update docs --- doc/charconv/from_chars.adoc | 26 ++++++++++++------------- doc/charconv/overview.adoc | 4 +++- doc/charconv/reference.adoc | 28 +++++++++++++-------------- doc/charconv/to_chars.adoc | 26 ++++++++++++------------- include/boost/charconv/from_chars.hpp | 12 ++++++------ test/from_chars_float.cpp | 3 ++- 6 files changed, 51 insertions(+), 48 deletions(-) diff --git a/doc/charconv/from_chars.adoc b/doc/charconv/from_chars.adoc index 14aedbf..f16c279 100644 --- a/doc/charconv/from_chars.adoc +++ b/doc/charconv/from_chars.adoc @@ -13,7 +13,7 @@ https://www.boost.org/LICENSE_1_0.txt struct from_chars_result { const char* ptr; - int ec; + std::errc ec; friend constexpr bool operator==(const from_chars_result& lhs, const from_chars_result& rhs) noexcept friend constexpr bool operator!=(const from_chars_result& lhs, const from_chars_result& rhs) noexcept @@ -30,10 +30,10 @@ from_chars_result from_chars(const char* first, const char* last, Real& value, c == from_chars_result * ptr - points to the first character not matching the pattern, or has the value of last if all characters are successfully parsed. -* ec - the error code. Valid values for are: -** 0 - successful parsing -** EINVAL - invalid argument (e.g. parsing a negative number into an unsigned type) -** ERANGE - result out of range (e.g. overflow) +* ec - the error code. Valid values for are: +** std::errc() - successful parsing +** std::errc::invalid_argument - invalid argument (e.g. parsing a negative number into an unsigned type) +** std::errc::result_out_of_range - result out of range (e.g. overflow) * operator== - compares the values of ptr and ec for equality * operator!- - compares the value of ptr and ec for inequality @@ -50,7 +50,7 @@ from_chars_result from_chars(const char* first, const char* last, Real& value, c ** One known exception is GCC 5 which does not support constexpr comparison of `const char*`. === from_chars for floating point types -* On ERANGE we return ±0 for small values (e.g. 1.0e-99999) or ±HUGE_VAL for large values (e.g. 1.0e+99999) to match the handling of `std::strtod`. +* On std::errc::result_out_of_range we return ±0 for small values (e.g. 1.0e-99999) or ±HUGE_VAL for large values (e.g. 1.0e+99999) to match the handling of `std::strtod`. This is a divergence from the standard which states we should return the `value` argument unmodified. == Examples @@ -62,7 +62,7 @@ This is a divergence from the standard which states we should return the `value` const char* buffer = "42"; int v = 0; from_chars_result r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(v == 42); ---- ==== Floating Point @@ -71,7 +71,7 @@ assert(v == 42); const char* buffer = "1.2345" double v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(v == 1.2345); ---- @@ -82,7 +82,7 @@ assert(v == 1.2345); const char* buffer = "2a"; unsigned v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v, 16); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(v == 42); ---- ==== Floating Point @@ -91,7 +91,7 @@ assert(v == 42); const char* buffer = "1.3a2bp-10"; double v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v, boost::charconv::chars_format::hex); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(v == 8.0427e-18); ---- @@ -101,14 +101,14 @@ assert(v == 8.0427e-18); const char* buffer = "-123"; unsigned v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); -assert(r.ec == EINVAL); +assert(r.ec == std::errc::invalid_argument); ---- [source, c++] ---- const char* buffer = "-1.573e-3"; double v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v, boost::charconv::chars_format::fixed); -assert(r.ec == EINVAL); +assert(r.ec == std::errc::invalid_argument); ---- In the event of EINVAL v is not set by `from_chars` @@ -118,7 +118,7 @@ In the event of EINVAL v is not set by `from_chars` const char* buffer = "1234"; unsigned char v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); -assert(r.ec == ERANGE); +assert(r.ec == std::errc::result_out_of_range); assert(v == 0) ---- In the event of ERANGE v is not set by `from_chars` diff --git a/doc/charconv/overview.adoc b/doc/charconv/overview.adoc index 8b3c5e8..a5d2cdb 100644 --- a/doc/charconv/overview.adoc +++ b/doc/charconv/overview.adoc @@ -22,11 +22,13 @@ This library requires a minimum of C++11. const char* buffer = "42"; int v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); -assert(r == 42); +assert(r.ec == std::errc()); +assert(v == 42); char buffer[64]; int v = 123456; auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v); +assert(r.ec == std::errc()); assert(!strncmp(buffer, "123456", 6)); // Strncmp returns 0 on match ---- diff --git a/doc/charconv/reference.adoc b/doc/charconv/reference.adoc index 778939c..eb70564 100644 --- a/doc/charconv/reference.adoc +++ b/doc/charconv/reference.adoc @@ -38,15 +38,15 @@ from_chars_result from_chars(const char* first, const char* last, Real& value, c struct from_chars_result { char const* ptr; - int ec; + std::errc ec; }; ---- `from_chars_result` is the return type of the `from_chars` family of overloaded functions. -The `ec` member is zero when the conversion was successful, `EINVAL` -when no prefix of the passed characters form a valid value, or `ERANGE` +The `ec` member is `std::errc()` when the conversion was successful, `std::errc::invalid_argument` +when no prefix of the passed characters form a valid value, or `std::errc::result_out_of_range` when the characters form a value that would be out of range for the type. The `ptr` member points to the first character not part of the matched @@ -66,8 +66,8 @@ Effects:;; Attempts to interpret the characters in `[first, last)` as a numeric consisting of an optional minus sign (only if the type is signed), and a sequence of digits. For bases above 10, the digit characters are `Aa` to `Zz`, as appropriate for `base`. -Returns:;; The `ec` member of the return value is `0` on success, `EINVAL` if - `[first, last)` can't be interpreted as an integer of base `base`, and `ERANGE` +Returns:;; The `ec` member of the return value is `std::errc()` on success, `std::errc::invalid_argument` if + `[first, last)` can't be interpreted as an integer of base `base`, and `std::errc::result_out_of_range` if `[first, last)` when interpreted as an integer of base `base` can't be represented as a value of type `Integral`. The `ptr` member of the return value points to the first character in `[first, last)` that is not part of the matched value, or is `last` when @@ -84,8 +84,8 @@ Requires:;; `fmt` has the value of one of the enumerators of chars_format Effects:;; value is converted to a string in the style of printf in the "C" locale with the given precision. If a precision is not provided the shortest representation will be given. Returns:;; The `ec` member of the return value is `0` on success. -`EINVAL` is returned if the value can't be interpreted with the given format `fmt`. -`ERANGE` is returned when the value can't be represented in the target floating point type. +`std::errc::invalid_argument` is returned if the value can't be interpreted with the given format `fmt`. +`std::errc::result_out_of_range` is returned when the value can't be represented in the target floating point type. The `ptr` member of the return value points to the first character in `[first, last)` that is not part of the matched value, or is `last` when all characters are matched. == @@ -117,18 +117,18 @@ to_chars_result to_chars(char* first, char* last, Real value, chars_format fmt = struct to_chars_result { char const* ptr; - int ec; + std::errc ec; }; ---- `to_chars_result` is the return type of the `to_chars` family of overloaded functions. -The `ec` member is zero when the conversion was successful, or `EOVERFLOW` +The `ec` member is `std::errc()` when the conversion was successful, or `std::errc::result_out_of_range` when the value cannot fit into the provided buffer. The `ptr` member points to the first character after the characters written, -or `last` when `ec` is `EOVERFLOW`. +or `last` when `ec` is `std::errc::result_out_of_range`. === to_chars @@ -145,10 +145,10 @@ Effects:;; The value of `value` is converted to a string of digits in the given Digits in the range 10..35 (inclusive) are represented as lowercase characters `a`..`z`. If value is less than zero, the representation starts with a minus sign. -Returns:;; The `ec` member of the return value is `0` on success, and `EOVERFLOW` if +Returns:;; The `ec` member of the return value is `std::errc()` on success, and `std::errc::result_out_of_range` if `[first, last)` does not contain enough space to hold the string representation of `value`. The `ptr` member of the return value points to the character in `[first, last]` - that is one past the storted characters, or is `last` when `ec` is `EOVERFLOW`. + that is one past the parsed characters, or is `last` when `ec` is `std::errc::result_out_of_range`. [source, c++] ---- @@ -161,10 +161,10 @@ Requires:;; fmt has the value of one of the enumerators of chars_format Effects:;; value is converted to a string in the style of printf in the "C" locale with the given precision. If no precision is provided the value character string will be the shortest representation of `value` -Returns:;; The `ec` member of the return value is `0` on success, and `EOVERFLOW` if +Returns:;; The `ec` member of the return value is `std::errc()` on success, and `std::errc::result_out_of_range` if `[first, last)` does not contain enough space to hold the string representation of `value`. The `ptr` member of the return value points to the character in `[first, last]` -that is one past the storted characters, or is `last` when `ec` is `EOVERFLOW`. +that is one past the parsed characters, or is `last` when `ec` is `std::errc::result_out_of_range`. == diff --git a/doc/charconv/to_chars.adoc b/doc/charconv/to_chars.adoc index eefa542..a1ad2bb 100644 --- a/doc/charconv/to_chars.adoc +++ b/doc/charconv/to_chars.adoc @@ -13,7 +13,7 @@ https://www.boost.org/LICENSE_1_0.txt struct to_chars_result { char* ptr; - int ec; + std::errc ec; friend constexpr bool operator==(const to_chars_result& lhs, const to_chars_result& rhs) noexcept; friend constexpr bool operator!=(const to_chars_result& lhs, const to_chars_result& rhs) noexcept; @@ -31,16 +31,16 @@ to_chars_result(char* first, char* last, Real value, chars_format fmt = chars_fo == to_chars_result * ptr - points to the first character -* ec - the error code. Valid values from are: +* ec - the error code. Valid values from are: ** 0 - successful parsing -** EINVAL - invalid argument -** ERANGE - result out of range (e.g. overflow) +** std::errc::invalid_argument - invalid argument +** std::errc::result_out_of_range - result out of range (e.g. overflow) * operator== - compares the value of ptr and ec for equality * operator!= - compares the value of ptr and ec for inequality == to_chars * first, last - pointers to the character buffer -* value - the value to be paresed into the buffer +* value - the value to be parsed into the buffer * base (integer only) - the integer base to use. Must be between 2 and 36 inclusive * fmt (float only) - the floating point format to use. See xref:chars_format.adoc[chars_format overview] for description. @@ -67,7 +67,7 @@ See xref:chars_format.adoc[chars_format overview] for description. char buffer[64] {}; int v = 42; to_chars_result r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(!strcmp(buffer, "42")); // strcmp returns 0 on match ---- ==== Floating Point @@ -76,7 +76,7 @@ assert(!strcmp(buffer, "42")); // strcmp returns 0 on match char buffer[64] {}; double v = 1e300; to_chars_result r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v); -assert(r.ex == 0); +assert(r.ec == std::errc()); assert(!strcmp(buffer, "1e+300")); ---- @@ -87,7 +87,7 @@ assert(!strcmp(buffer, "1e+300")); char buffer[64] {}; int v = 42; to_chars_result r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v, 16); -assert(r.ec == 0); +assert(r.ec == std::errc()); assert(!strcmp(buffer, "2a")); // strcmp returns 0 on match ---- ==== Floating Point @@ -96,18 +96,18 @@ assert(!strcmp(buffer, "2a")); // strcmp returns 0 on match char buffer[64] {}; double v = -1.08260383390082946e+307; to_chars_result r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v, boost::charconv::chars_format::hex); -assert(r.ex == 0); +assert(r.ec == std::errc()); assert(!strcmp(buffer, "-1.ed5658af91a0fp+1019")); ---- -=== ERANGE +=== std::errc::result_out_of_range ==== Integral [source, c++] ---- char buffer[3] {}; int v = -1234; to_chars_result r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v, 16); -assert(r.ec == ERANGE); +assert(r.ec == std::errc::result_out_of_range); ---- ==== Floating Point [source, c++] @@ -115,7 +115,7 @@ assert(r.ec == ERANGE); char buffer[3] {}; double v = 1.2345; auto r = boost::charconv::to_chars(buffer, buffer + sizeof(buffer) - 1, v); -assert(r.ec == ERANGE); +assert(r.ec == std::errc::result_out_of_range); ---- -In the event of ERANGE to_chars_result.ptr is first +In the event of std::errc::result_out_of_range to_chars_result.ptr is equal to first diff --git a/include/boost/charconv/from_chars.hpp b/include/boost/charconv/from_chars.hpp index 8dda4cf..0e5f272 100644 --- a/include/boost/charconv/from_chars.hpp +++ b/include/boost/charconv/from_chars.hpp @@ -183,12 +183,12 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& if (return_val == HUGE_VALF || return_val == -HUGE_VALF) { value = return_val; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else if (exponent < -46) { value = sign ? -0.0F : 0.0; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else { @@ -200,12 +200,12 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& if (return_val == HUGE_VAL || return_val == -HUGE_VAL) { value = return_val; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else if (exponent < -325) { value = sign ? -0.0 : 0.0; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else { @@ -217,7 +217,7 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& if (return_val == HUGE_VALL || return_val == -HUGE_VALL) { value = return_val; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } #if BOOST_CHARCONV_LDBL_BITS == 64 else if (exponent < -325) @@ -226,7 +226,7 @@ from_chars_result from_chars_float_impl(const char* first, const char* last, T& #endif { value = sign ? -0.0L : 0.0L; - r.ec = ERANGE; + r.ec = std::errc::result_out_of_range; } else diff --git a/test/from_chars_float.cpp b/test/from_chars_float.cpp index bdf265f..5d5b1e1 100644 --- a/test/from_chars_float.cpp +++ b/test/from_chars_float.cpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include @@ -30,7 +31,7 @@ void overflow_spot_value(const std::string& buffer, T expected_value, boost::cha auto v = static_cast(42.L); auto r = boost::charconv::from_chars(buffer.c_str(), buffer.c_str() + std::strlen(buffer.c_str()), v, fmt); - if (!(BOOST_TEST_EQ(v, expected_value) && BOOST_TEST_EQ(r.ec, ERANGE))) + if (!(BOOST_TEST_EQ(v, expected_value) && BOOST_TEST(r.ec == std::errc::result_out_of_range))) { std::cerr << "Test failure for: " << buffer << " got: " << v << std::endl; } From 692a2b9c0151abdc676e3bbb44e72dedb5c1afe4 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 11:20:49 +0200 Subject: [PATCH 5/7] Fix failures on platforms with long double > 64 bits --- src/from_chars.cpp | 2 +- src/to_chars.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/from_chars.cpp b/src/from_chars.cpp index 3d967ed..272579e 100644 --- a/src/from_chars.cpp +++ b/src/from_chars.cpp @@ -121,7 +121,7 @@ boost::charconv::from_chars_result boost::charconv::from_chars(const char* first value = std::strtold( tmp.c_str(), &ptr ); r.ptr = ptr; - r.ec = errno; + r.ec = detail::errno_to_errc(errno); return r; } diff --git a/src/to_chars.cpp b/src/to_chars.cpp index af8670e..7330a56 100644 --- a/src/to_chars.cpp +++ b/src/to_chars.cpp @@ -578,6 +578,6 @@ boost::charconv::to_chars_result boost::charconv::to_chars(char* first, char* la boost::charconv::to_chars_result boost::charconv::to_chars( char* first, char* last, long double value ) noexcept { std::snprintf( first, last - first, "%.*Lg", std::numeric_limits::max_digits10, value ); - return { first + std::strlen(first), 0 }; + return { first + std::strlen(first), std::errc() }; } #endif From 21f46c136f2fccdc4c148040f3d53014b4454e61 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 11:29:53 +0200 Subject: [PATCH 6/7] Fix doc section headers --- doc/charconv/from_chars.adoc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/charconv/from_chars.adoc b/doc/charconv/from_chars.adoc index f16c279..e27cd87 100644 --- a/doc/charconv/from_chars.adoc +++ b/doc/charconv/from_chars.adoc @@ -95,7 +95,7 @@ assert(r.ec == std::errc()); assert(v == 8.0427e-18); ---- -=== EINVAL +=== std::errc::invalid_argument [source, c++] ---- const char* buffer = "-123"; @@ -110,9 +110,9 @@ double v = 0; auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v, boost::charconv::chars_format::fixed); assert(r.ec == std::errc::invalid_argument); ---- -In the event of EINVAL v is not set by `from_chars` +Note: In the event of std::errc::invalid_argument v is not modified by `from_chars` -=== ERANGE +=== std::errc::result_out_of_range [source, c++] ---- const char* buffer = "1234"; @@ -121,4 +121,4 @@ auto r = boost::charconv::from_chars(buffer, buffer + std::strlen(buffer), v); assert(r.ec == std::errc::result_out_of_range); assert(v == 0) ---- -In the event of ERANGE v is not set by `from_chars` +Note: In the event of std::errc::result_out_of_range v is not modified by `from_chars` From 554d7a0c3e74adfc860b9c31f3e7ab91c639bdc3 Mon Sep 17 00:00:00 2001 From: Matt Borland Date: Thu, 18 May 2023 12:43:11 +0200 Subject: [PATCH 7/7] Ignore GCC 9, 10, and 12 -Wmaybe-uninitialized --- .../boost/charconv/detail/from_chars_integer_impl.hpp | 6 +++++- test/roundtrip.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/include/boost/charconv/detail/from_chars_integer_impl.hpp b/include/boost/charconv/detail/from_chars_integer_impl.hpp index ca57d04..e893ffd 100644 --- a/include/boost/charconv/detail/from_chars_integer_impl.hpp +++ b/include/boost/charconv/detail/from_chars_integer_impl.hpp @@ -59,6 +59,10 @@ constexpr unsigned char digit_from_char(char val) noexcept # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Woverflow" +#elif defined(__GNUC__) && (__GNUC__ >= 9) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" + #endif template @@ -210,7 +214,7 @@ BOOST_CXX14_CONSTEXPR from_chars_result from_chars_integer_impl(const char* firs # pragma warning(pop) #elif defined(__clang__) && defined(__APPLE__) # pragma clang diagnostic pop -#elif defined(__GNUC__) && (__GNUC__ < 7) +#elif defined(__GNUC__) && (__GNUC__ < 7 || __GNUC__ >= 9) # pragma GCC diagnostic pop #endif diff --git a/test/roundtrip.cpp b/test/roundtrip.cpp index 7788788..d85cfd0 100644 --- a/test/roundtrip.cpp +++ b/test/roundtrip.cpp @@ -73,6 +73,11 @@ static boost::detail::splitmix64 rng; // integral types, random values +#if defined(__GNUC__) && (__GNUC__ == 12) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +#endif + template void test_roundtrip( T value, int base ) { char buffer[ 256 ]; @@ -93,6 +98,10 @@ template void test_roundtrip( T value, int base ) } } +#if defined(__GNUC__) && (__GNUC__ == 12) +# pragma GCC diagnostic pop +#endif + template void test_roundtrip_int8( int base ) { for( int i = -256; i <= 255; ++i )