diff --git a/include/ss/extract.hpp b/include/ss/extract.hpp index 52aa0f9..ae2d81c 100644 --- a/include/ss/extract.hpp +++ b/include/ss/extract.hpp @@ -2,7 +2,6 @@ #include "type_traits.hpp" #include -#include #include #include #include @@ -11,13 +10,18 @@ #include #include +#ifndef SSP_DISABLE_FAST_FLOAT +#include +#else +#include +#endif + namespace ss { //////////////// // number converters //////////////// -#define SSP_DISABLE_FAST_FLOAT #ifndef SSP_DISABLE_FAST_FLOAT template @@ -38,20 +42,11 @@ template std::enable_if_t, std::optional> to_num( const char* const begin, const char* const end) { T ret; - try { - if constexpr (std::is_same_v) { - ret = std::stof(std::string{begin, end}); - } - if constexpr (std::is_same_v) { - ret = std::stod(std::string{begin, end}); - } - if constexpr (std::is_same_v) { - ret = std::stold(std::string{begin, end}); - } - } catch (...) { + auto [ptr, ec] = std::from_chars(begin, end, ret); + + if (ec != std::errc() || ptr != end) { return std::nullopt; } - return ret; } diff --git a/test/meson.build b/test/meson.build index a8596eb..e2d33b1 100644 --- a/test/meson.build +++ b/test/meson.build @@ -4,6 +4,7 @@ test_sources = files([ 'test_converter.cpp', 'test_parser.cpp', 'test_extractions.cpp', + 'test_extractions_without_fast_float.cpp', ]) doctest_proj = subproject('doctest') diff --git a/test/test_extractions.cpp b/test/test_extractions.cpp index 1650289..bda33b4 100644 --- a/test/test_extractions.cpp +++ b/test/test_extractions.cpp @@ -2,23 +2,6 @@ #include #include -#define CHECK_FLOATING_CONVERSION(input, type) \ - { \ - auto eps = std::numeric_limits::min(); \ - std::string s = #input; \ - auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - REQUIRE(t.has_value()); \ - CHECK_LT(std::abs(t.value() - type(input)), eps); \ - } \ - { \ - /* check negative too */ \ - auto eps = std::numeric_limits::min(); \ - auto s = std::string("-") + #input; \ - auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - REQUIRE(t.has_value()); \ - CHECK_LT(std::abs(t.value() - type(-input)), eps); \ - } - TEST_CASE("testing extract functions for floating point values") { CHECK_FLOATING_CONVERSION(123.456, float); CHECK_FLOATING_CONVERSION(123.456, double); @@ -70,13 +53,6 @@ TEST_CASE("extract test functions for decimal values") { CHECK_DECIMAL_CONVERSION(1234567891011, ull); } -#define CHECK_INVALID_CONVERSION(input, type) \ - { \ - std::string s = input; \ - auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ - CHECK_FALSE(t.has_value()); \ - } - TEST_CASE("extract test functions for numbers with invalid inputs") { // negative unsigned value CHECK_INVALID_CONVERSION("-1234", ul); @@ -200,15 +176,6 @@ TEST_CASE("extract test functions for std::optional") { } } -#define REQUIRE_VARIANT(var, el, type) \ - { \ - auto ptr = std::get_if(&var); \ - REQUIRE(ptr); \ - REQUIRE_EQ(el, *ptr); \ - } - -#define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative(var)); - TEST_CASE("extract test functions for std::variant") { { std::string s = "22"; diff --git a/test/test_extractions_without_fast_float.cpp b/test/test_extractions_without_fast_float.cpp new file mode 100644 index 0000000..3f5cd40 --- /dev/null +++ b/test/test_extractions_without_fast_float.cpp @@ -0,0 +1,132 @@ +#include "test_helpers.hpp" +#include + +#define SSP_DISABLE_FAST_FLOAT +#include + +TEST_CASE( + "testing extract functions for floating point values without fast float") { + CHECK_FLOATING_CONVERSION(123.456, float); + CHECK_FLOATING_CONVERSION(123.456, double); + + CHECK_FLOATING_CONVERSION(69, float); + CHECK_FLOATING_CONVERSION(69, double); + + CHECK_FLOATING_CONVERSION(420., float); + CHECK_FLOATING_CONVERSION(420., double); + + CHECK_FLOATING_CONVERSION(0.123, float); + CHECK_FLOATING_CONVERSION(0.123, double); + + CHECK_FLOATING_CONVERSION(123e4, float); + CHECK_FLOATING_CONVERSION(123e4, double); +} + +TEST_CASE("extract test functions for numbers with invalid inputs without fast " + "float") { + // floating pint for int + CHECK_INVALID_CONVERSION("123.4", int); + + // random input for float + CHECK_INVALID_CONVERSION("xxx1", float); +} + +TEST_CASE("extract test functions for std::variant without fast float") { + { + std::string s = "22"; + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, double); + CHECK_NOT_VARIANT(var, std::string); + REQUIRE_VARIANT(var, 22, int); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, std::string); + REQUIRE_VARIANT(var, 22, double); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, double); + REQUIRE_VARIANT(var, "22", std::string); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + REQUIRE_VARIANT(var, 22, int); + } + } + { + std::string s = "22.2"; + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, std::string); + REQUIRE_VARIANT(var, 22.2, double); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, std::string); + REQUIRE_VARIANT(var, 22.2, double); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, double); + REQUIRE_VARIANT(var, "22.2", std::string); + } + } + { + std::string s = "2.2.2"; + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, double); + REQUIRE_VARIANT(var, "2.2.2", std::string); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, double); + REQUIRE_VARIANT(var, "2.2.2", std::string); + } + { + std::variant var; + REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + CHECK_NOT_VARIANT(var, int); + CHECK_NOT_VARIANT(var, double); + REQUIRE_VARIANT(var, "2.2.2", std::string); + } + { + std::variant var; + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + + REQUIRE_VARIANT(var, int{}, int); + CHECK_NOT_VARIANT(var, double); + } + { + std::variant var; + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + + REQUIRE_VARIANT(var, double{}, double); + CHECK_NOT_VARIANT(var, int); + } + { + std::variant var; + REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var)); + + REQUIRE_VARIANT(var, int{}, int); + } + } +} diff --git a/test/test_helpers.hpp b/test/test_helpers.hpp index 03f2750..06ea703 100644 --- a/test/test_helpers.hpp +++ b/test/test_helpers.hpp @@ -46,3 +46,36 @@ struct buffer { }; [[maybe_unused]] inline buffer buff; + +#define CHECK_FLOATING_CONVERSION(input, type) \ + { \ + auto eps = std::numeric_limits::min(); \ + std::string s = #input; \ + auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ + REQUIRE(t.has_value()); \ + CHECK_LT(std::abs(t.value() - type(input)), eps); \ + } \ + { \ + /* check negative too */ \ + auto eps = std::numeric_limits::min(); \ + auto s = std::string("-") + #input; \ + auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ + REQUIRE(t.has_value()); \ + CHECK_LT(std::abs(t.value() - type(-input)), eps); \ + } + +#define CHECK_INVALID_CONVERSION(input, type) \ + { \ + std::string s = input; \ + auto t = ss::to_num(s.c_str(), s.c_str() + s.size()); \ + CHECK_FALSE(t.has_value()); \ + } + +#define REQUIRE_VARIANT(var, el, type) \ + { \ + auto ptr = std::get_if(&var); \ + REQUIRE(ptr); \ + REQUIRE_EQ(el, *ptr); \ + } + +#define CHECK_NOT_VARIANT(var, type) CHECK(!std::holds_alternative(var));