Add ss::uint8 and ss::int8, add unit tests for them

This commit is contained in:
ado 2024-02-25 17:46:35 +01:00
parent f8e14b1fcf
commit 0a695cf09e
4 changed files with 294 additions and 189 deletions

View File

@ -77,6 +77,38 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
#endif
////////////////
// numeric_wrapper
////////////////
template <typename T>
struct numeric_wrapper {
using type = T;
numeric_wrapper() = default;
numeric_wrapper(numeric_wrapper&&) = default;
numeric_wrapper(const numeric_wrapper&) = default;
numeric_wrapper& operator=(numeric_wrapper&&) = default;
numeric_wrapper& operator=(const numeric_wrapper&) = default;
numeric_wrapper(T other) : value{other} {
}
operator T() {
return value;
}
operator T() const {
return value;
}
T value;
};
using int8 = numeric_wrapper<int8_t>;
using uint8 = numeric_wrapper<uint8_t>;
template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) {
@ -89,6 +121,18 @@ std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
return ret;
}
template <typename T>
std::enable_if_t<is_instance_of_v<numeric_wrapper, T>, std::optional<T>> to_num(
const char* const begin, const char* const end) {
T ret;
auto [ptr, ec] = std::from_chars(begin, end, ret.value);
if (ec != std::errc() || ptr != end) {
return std::nullopt;
}
return ret;
}
////////////////
// extract
////////////////
@ -103,7 +147,8 @@ struct unsupported_type {
template <typename T>
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
!is_instance_of_v<std::optional, T> &&
!is_instance_of_v<std::variant, T>,
!is_instance_of_v<std::variant, T> &&
!is_instance_of_v<numeric_wrapper, T>,
bool>
extract(const char*, const char*, T&) {
static_assert(error::unsupported_type<T>::value,
@ -112,7 +157,9 @@ extract(const char*, const char*, T&) {
}
template <typename T>
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool>
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T> ||
is_instance_of_v<numeric_wrapper, T>,
bool>
extract(const char* begin, const char* end, T& value) {
auto optional_value = to_num<T>(begin, end);
if (!optional_value) {

51
ssp.hpp
View File

@ -1531,6 +1531,38 @@ std::enable_if_t<std::is_floating_point_v<T>, std::optional<T>> to_num(
#endif
////////////////
// numeric_wrapper
////////////////
template <typename T>
struct numeric_wrapper {
using type = T;
numeric_wrapper() = default;
numeric_wrapper(numeric_wrapper&&) = default;
numeric_wrapper(const numeric_wrapper&) = default;
numeric_wrapper& operator=(numeric_wrapper&&) = default;
numeric_wrapper& operator=(const numeric_wrapper&) = default;
numeric_wrapper(T other) : value{other} {
}
operator T() {
return value;
}
operator T() const {
return value;
}
T value;
};
using int8 = numeric_wrapper<int8_t>;
using uint8 = numeric_wrapper<uint8_t>;
template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) {
@ -1543,6 +1575,18 @@ std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
return ret;
}
template <typename T>
std::enable_if_t<is_instance_of_v<numeric_wrapper, T>, std::optional<T>> to_num(
const char* const begin, const char* const end) {
T ret;
auto [ptr, ec] = std::from_chars(begin, end, ret.value);
if (ec != std::errc() || ptr != end) {
return std::nullopt;
}
return ret;
}
////////////////
// extract
////////////////
@ -1557,7 +1601,8 @@ struct unsupported_type {
template <typename T>
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
!is_instance_of_v<std::optional, T> &&
!is_instance_of_v<std::variant, T>,
!is_instance_of_v<std::variant, T> &&
!is_instance_of_v<numeric_wrapper, T>,
bool>
extract(const char*, const char*, T&) {
static_assert(error::unsupported_type<T>::value,
@ -1566,7 +1611,9 @@ extract(const char*, const char*, T&) {
}
template <typename T>
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T>, bool>
std::enable_if_t<std::is_integral_v<T> || std::is_floating_point_v<T> ||
is_instance_of_v<numeric_wrapper, T>,
bool>
extract(const char* begin, const char* end, T& value) {
auto optional_value = to_num<T>(begin, end);
if (!optional_value) {

View File

@ -46,90 +46,88 @@ TEST_CASE("converter test split with exceptions") {
}
}
TEST_CASE("converter test valid conversions") {
TEST_CASE_TEMPLATE("converter test valid conversions", T, int, ss::uint8) {
ss::converter c;
{
auto tup = c.convert<int>("5");
auto tup = c.convert<T>("5");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<int, void>("5,junk");
auto tup = c.convert<T, void>("5,junk");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<void, int>("junk,5");
auto tup = c.convert<void, T>("junk,5");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<int, void, void>("5\njunk\njunk", "\n");
auto tup = c.convert<T, void, void>("5\njunk\njunk", "\n");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<void, int, void>("junk 5 junk", " ");
auto tup = c.convert<void, T, void>("junk 5 junk", " ");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<void, void, int>("junk\tjunk\t5", "\t");
auto tup = c.convert<void, void, T>("junk\tjunk\t5", "\t");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
}
{
auto tup =
c.convert<void, void, std::optional<int>>("junk\tjunk\t5", "\t");
c.convert<void, void, std::optional<T>>("junk\tjunk\t5", "\t");
REQUIRE(c.valid());
REQUIRE(tup.has_value());
CHECK_EQ(tup, 5);
}
{
auto tup = c.convert<int, double, void>("5,6.6,junk");
auto tup = c.convert<T, double, void>("5,6.6,junk");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
}
{
auto tup = c.convert<int, void, double>("5,junk,6.6");
auto tup = c.convert<T, void, double>("5,junk,6.6");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
}
{
auto tup = c.convert<void, int, double>("junk;5;6.6", ";");
auto tup = c.convert<void, T, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
}
{
auto tup =
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
REQUIRE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
}
{
auto tup =
c.convert<void, std::optional<int>, double>("junk;5.4;6.6", ";");
c.convert<void, std::optional<T>, double>("junk;5.4;6.6", ";");
REQUIRE(c.valid());
REQUIRE_FALSE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(std::optional<int>{}, 6.6));
CHECK_EQ(tup, std::make_tuple(std::optional<T>{}, 6.6));
}
{
auto tup =
c.convert<void, std::variant<int, double>, double>("junk;5;6.6",
";");
c.convert<void, std::variant<T, double>, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
REQUIRE(std::holds_alternative<int>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6));
REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5}, 6.6));
}
{
auto tup =
c.convert<void, std::variant<int, double>, double>("junk;5.5;6.6",
c.convert<void, std::variant<T, double>, double>("junk;5.5;6.6",
";");
REQUIRE(c.valid());
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5.5}, 6.6));
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5.5}, 6.6));
}
{
auto tup = c.convert<void, std::string_view, double,
@ -140,11 +138,12 @@ TEST_CASE("converter test valid conversions") {
}
}
TEST_CASE("converter test valid conversions with exceptions") {
TEST_CASE_TEMPLATE("converter test valid conversions with exceptions", T, int,
ss::uint8) {
ss::converter<ss::throw_on_error> c;
try {
auto tup = c.convert<int>("5");
auto tup = c.convert<T>("5");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -152,7 +151,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<int, void>("5,junk");
auto tup = c.convert<T, void>("5,junk");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -160,7 +159,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<void, int>("junk,5");
auto tup = c.convert<void, T>("junk,5");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -168,7 +167,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<int, void, void>("5\njunk\njunk", "\n");
auto tup = c.convert<T, void, void>("5\njunk\njunk", "\n");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -176,7 +175,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<void, int, void>("junk 5 junk", " ");
auto tup = c.convert<void, T, void>("junk 5 junk", " ");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -184,7 +183,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<void, void, int>("junk\tjunk\t5", "\t");
auto tup = c.convert<void, void, T>("junk\tjunk\t5", "\t");
REQUIRE(c.valid());
CHECK_EQ(tup, 5);
} catch (ss::exception& e) {
@ -193,7 +192,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
try {
auto tup =
c.convert<void, void, std::optional<int>>("junk\tjunk\t5", "\t");
c.convert<void, void, std::optional<T>>("junk\tjunk\t5", "\t");
REQUIRE(c.valid());
REQUIRE(tup.has_value());
CHECK_EQ(tup, 5);
@ -202,7 +201,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<int, double, void>("5,6.6,junk");
auto tup = c.convert<T, double, void>("5,6.6,junk");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) {
@ -210,7 +209,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<int, void, double>("5,junk,6.6");
auto tup = c.convert<T, void, double>("5,junk,6.6");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) {
@ -218,7 +217,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup = c.convert<void, int, double>("junk;5;6.6", ";");
auto tup = c.convert<void, T, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) {
@ -226,8 +225,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
try {
auto tup =
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
REQUIRE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(5, 6.6));
@ -237,32 +235,31 @@ TEST_CASE("converter test valid conversions with exceptions") {
try {
auto tup =
c.convert<void, std::optional<int>, double>("junk;5.4;6.6", ";");
c.convert<void, std::optional<T>, double>("junk;5.4;6.6", ";");
REQUIRE(c.valid());
REQUIRE_FALSE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(std::optional<int>{}, 6.6));
CHECK_EQ(tup, std::make_tuple(std::optional<T>{}, 6.6));
} catch (ss::exception& e) {
FAIL(std::string{e.what()});
}
try {
auto tup =
c.convert<void, std::variant<int, double>, double>("junk;5;6.6",
";");
c.convert<void, std::variant<T, double>, double>("junk;5;6.6", ";");
REQUIRE(c.valid());
REQUIRE(std::holds_alternative<int>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6));
REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5}, 6.6));
} catch (ss::exception& e) {
FAIL(std::string{e.what()});
}
try {
auto tup =
c.convert<void, std::variant<int, double>, double>("junk;5.5;6.6",
c.convert<void, std::variant<T, double>, double>("junk;5.5;6.6",
";");
REQUIRE(c.valid());
REQUIRE(std::holds_alternative<double>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5.5}, 6.6));
CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5.5}, 6.6));
} catch (ss::exception& e) {
FAIL(std::string{e.what()});
}
@ -278,110 +275,114 @@ TEST_CASE("converter test valid conversions with exceptions") {
}
}
TEST_CASE("converter test invalid conversions") {
TEST_CASE_TEMPLATE("converter test invalid conversions", T, int, ss::uint8) {
ss::converter c;
c.convert<int>("");
c.convert<T>("");
REQUIRE_FALSE(c.valid());
c.convert<int>("1", "");
c.convert<T>("1", "");
REQUIRE_FALSE(c.valid());
c.convert<int>("10", "");
c.convert<T>("10", "");
REQUIRE_FALSE(c.valid());
c.convert<int, void>("");
c.convert<T, void>("");
REQUIRE_FALSE(c.valid());
c.convert<int, void>(",junk");
c.convert<T, void>(",junk");
REQUIRE_FALSE(c.valid());
c.convert<void, int>("junk,");
c.convert<void, T>("junk,");
REQUIRE_FALSE(c.valid());
c.convert<int>("x");
c.convert<T>("x");
REQUIRE_FALSE(c.valid());
c.convert<int, void>("x");
c.convert<T, void>("x");
REQUIRE_FALSE(c.valid());
c.convert<int, void>("x,junk");
c.convert<T, void>("x,junk");
REQUIRE_FALSE(c.valid());
c.convert<void, int>("junk,x");
c.convert<void, T>("junk,x");
REQUIRE_FALSE(c.valid());
c.convert<void, std::variant<int, double>, double>("junk;.5.5;6", ";");
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";");
REQUIRE_FALSE(c.valid());
}
TEST_CASE("converter test invalid conversions with exceptions") {
TEST_CASE_TEMPLATE("converter test invalid conversions with exceptions", T, int,
ss::uint8) {
ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<int>(""));
REQUIRE_EXCEPTION(c.convert<int>("1", ""));
REQUIRE_EXCEPTION(c.convert<int>("10", ""));
REQUIRE_EXCEPTION(c.convert<int, void>(""));
REQUIRE_EXCEPTION(c.convert<int, void>(",junk"));
REQUIRE_EXCEPTION(c.convert<void, int>("junk,"));
REQUIRE_EXCEPTION(c.convert<int>("x"));
REQUIRE_EXCEPTION(c.convert<int, void>("x"));
REQUIRE_EXCEPTION(c.convert<int, void>("x,junk"));
REQUIRE_EXCEPTION(c.convert<void, int>("junk,x"));
REQUIRE_EXCEPTION(c.convert<T>(""));
REQUIRE_EXCEPTION(c.convert<T>("1", ""));
REQUIRE_EXCEPTION(c.convert<T>("10", ""));
REQUIRE_EXCEPTION(c.convert<T, void>(""));
REQUIRE_EXCEPTION(c.convert<T, void>(",junk"));
REQUIRE_EXCEPTION(c.convert<void, T>("junk,"));
REQUIRE_EXCEPTION(c.convert<T>("x"));
REQUIRE_EXCEPTION(c.convert<T, void>("x"));
REQUIRE_EXCEPTION(c.convert<T, void>("x,junk"));
REQUIRE_EXCEPTION(c.convert<void, T>("junk,x"));
REQUIRE_EXCEPTION(
c.convert<void, std::variant<int, double>, double>("junk;.5.5;6", ";"));
c.convert<void, std::variant<T, double>, double>("junk;.5.5;6", ";"));
}
TEST_CASE("converter test ss:ax restriction (all except)") {
TEST_CASE_TEMPLATE("converter test ss:ax restriction (all except)", T, int,
ss::uint8) {
ss::converter c;
c.convert<ss::ax<int, 0>>("0");
c.convert<ss::ax<T, 0>>("0");
REQUIRE_FALSE(c.valid());
c.convert<ss::ax<int, 0, 1, 2>>("1");
c.convert<ss::ax<T, 0, 1, 2>>("1");
REQUIRE_FALSE(c.valid());
c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1");
c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1");
REQUIRE_FALSE(c.valid());
c.convert<ss::ax<int, 1>, char>("1,c");
c.convert<ss::ax<T, 1>, char>("1,c");
REQUIRE_FALSE(c.valid());
{
int tup = c.convert<ss::ax<int, 1>>("3");
T tup = c.convert<ss::ax<T, 1>>("3");
REQUIRE(c.valid());
CHECK_EQ(tup, 3);
}
{
std::tuple<char, int> tup = c.convert<char, ss::ax<int, 1>>("c,3");
std::tuple<char, T> tup = c.convert<char, ss::ax<T, 1>>("c,3");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('c', 3));
}
{
std::tuple<int, char> tup = c.convert<ss::ax<int, 1>, char>("3,c");
std::tuple<T, char> tup = c.convert<ss::ax<T, 1>, char>("3,c");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(3, 'c'));
}
}
TEST_CASE("converter test ss:ax restriction (all except) with exceptions") {
TEST_CASE_TEMPLATE(
"converter test ss:ax restriction (all except) with exceptions", T, int,
ss::uint8) {
ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0>>("0"));
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0, 1, 2>>("1"));
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1"));
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 1>, char>("1,c"));
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0>>("0"));
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0, 1, 2>>("1"));
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1"));
REQUIRE_EXCEPTION(c.convert<ss::ax<T, 1>, char>("1,c"));
try {
{
int tup = c.convert<ss::ax<int, 1>>("3");
T tup = c.convert<ss::ax<T, 1>>("3");
CHECK_EQ(tup, 3);
}
{
std::tuple<char, int> tup = c.convert<char, ss::ax<int, 1>>("c,3");
std::tuple<char, T> tup = c.convert<char, ss::ax<T, 1>>("c,3");
CHECK_EQ(tup, std::make_tuple('c', 3));
}
{
std::tuple<int, char> tup = c.convert<ss::ax<int, 1>, char>("3,c");
std::tuple<T, char> tup = c.convert<ss::ax<T, 1>, char>("3,c");
CHECK_EQ(tup, std::make_tuple(3, 'c'));
}
} catch (ss::exception& e) {
@ -456,65 +457,68 @@ TEST_CASE("converter test ss:nx restriction (none except) with exceptions") {
}
}
TEST_CASE("converter test ss:ir restriction (in range)") {
TEST_CASE_TEMPLATE("converter test ss:ir restriction (in range)", T, int,
ss::uint8) {
ss::converter c;
c.convert<ss::ir<int, 0, 2>>("3");
c.convert<ss::ir<T, 0, 2>>("3");
REQUIRE_FALSE(c.valid());
c.convert<char, ss::ir<int, 4, 69>>("c,3");
c.convert<char, ss::ir<T, 4, 69>>("c,3");
REQUIRE_FALSE(c.valid());
c.convert<ss::ir<int, 1, 2>, char>("3,c");
c.convert<ss::ir<T, 1, 2>, char>("3,c");
REQUIRE_FALSE(c.valid());
{
auto tup = c.convert<ss::ir<int, 1, 5>>("3");
auto tup = c.convert<ss::ir<T, 1, 5>>("3");
REQUIRE(c.valid());
CHECK_EQ(tup, 3);
}
{
auto tup = c.convert<ss::ir<int, 0, 2>>("2");
auto tup = c.convert<ss::ir<T, 0, 2>>("2");
REQUIRE(c.valid());
CHECK_EQ(tup, 2);
}
{
auto tup = c.convert<char, void, ss::ir<int, 0, 1>>("c,junk,1");
auto tup = c.convert<char, void, ss::ir<T, 0, 1>>("c,junk,1");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('c', 1));
}
{
auto tup = c.convert<ss::ir<int, 1, 20>, char>("1,c");
auto tup = c.convert<ss::ir<T, 1, 20>, char>("1,c");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(1, 'c'));
}
}
TEST_CASE("converter test ss:ir restriction (in range) with exceptions") {
TEST_CASE_TEMPLATE(
"converter test ss:ir restriction (in range) with exceptions", T, int,
ss::uint8) {
ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 0, 2>>("3"));
REQUIRE_EXCEPTION(c.convert<char, ss::ir<int, 4, 69>>("c,3"));
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 1, 2>, char>("3,c"));
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 0, 2>>("3"));
REQUIRE_EXCEPTION(c.convert<char, ss::ir<T, 4, 69>>("c,3"));
REQUIRE_EXCEPTION(c.convert<ss::ir<T, 1, 2>, char>("3,c"));
try {
{
auto tup = c.convert<ss::ir<int, 1, 5>>("3");
auto tup = c.convert<ss::ir<T, 1, 5>>("3");
REQUIRE(c.valid());
CHECK_EQ(tup, 3);
}
{
auto tup = c.convert<ss::ir<int, 0, 2>>("2");
auto tup = c.convert<ss::ir<T, 0, 2>>("2");
REQUIRE(c.valid());
CHECK_EQ(tup, 2);
}
{
auto tup = c.convert<char, void, ss::ir<int, 0, 1>>("c,junk,1");
auto tup = c.convert<char, void, ss::ir<T, 0, 1>>("c,junk,1");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('c', 1));
}
{
auto tup = c.convert<ss::ir<int, 1, 20>, char>("1,c");
auto tup = c.convert<ss::ir<T, 1, 20>, char>("1,c");
REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(1, 'c'));
}
@ -978,4 +982,3 @@ TEST_CASE("converter test invalid split conversions with exceptions") {
buff(R"(just,some,2,"strings\")")));
CHECK(c.unterminated_quote());
}

View File

@ -2,6 +2,16 @@
#include <algorithm>
#include <ss/extract.hpp>
template <typename T>
struct std::numeric_limits<ss::numeric_wrapper<T>>
: public std::numeric_limits<T> {};
template <typename T>
struct std::is_signed<ss::numeric_wrapper<T>> : public std::is_signed<T> {};
template <typename T>
struct std::is_unsigned<ss::numeric_wrapper<T>> : public std::is_unsigned<T> {};
TEST_CASE("testing extract functions for floating point values") {
CHECK_FLOATING_CONVERSION(123.456, float);
CHECK_FLOATING_CONVERSION(123.456, double);
@ -22,18 +32,18 @@ TEST_CASE("testing extract functions for floating point values") {
#define CHECK_DECIMAL_CONVERSION(input, type) \
{ \
std::string s = #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_EQ(t.value(), type(input)); \
type value; \
bool valid = ss::extract(s.c_str(), s.c_str() + s.size(), value); \
REQUIRE(valid); \
CHECK_EQ(value, type(input)); \
} \
{ \
/* check negative too */ \
if (std::is_signed_v<type>) { \
auto s = std::string("-") + #input; \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
REQUIRE(t.has_value()); \
CHECK_EQ(t.value(), type(-input)); \
} \
std::string s = std::string("-") + #input; \
type value; \
bool valid = ss::extract(s.c_str(), s.c_str() + s.size(), value); \
REQUIRE(valid); \
CHECK_EQ(value, type(-input)); \
}
using us = unsigned short;
@ -43,6 +53,8 @@ using ll = long long;
using ull = unsigned long long;
TEST_CASE("extract test functions for decimal values") {
CHECK_DECIMAL_CONVERSION(12, ss::int8);
CHECK_DECIMAL_CONVERSION(12, ss::uint8);
CHECK_DECIMAL_CONVERSION(1234, short);
CHECK_DECIMAL_CONVERSION(1234, us);
CHECK_DECIMAL_CONVERSION(1234, int);
@ -54,6 +66,9 @@ TEST_CASE("extract test functions for decimal values") {
}
TEST_CASE("extract test functions for numbers with invalid inputs") {
// negative unsigned value for numeric_wrapper
CHECK_INVALID_CONVERSION("-12", ss::uint8);
// negative unsigned value
CHECK_INVALID_CONVERSION("-1234", ul);
@ -70,46 +85,38 @@ TEST_CASE("extract test functions for numbers with invalid inputs") {
CHECK_INVALID_CONVERSION("", int);
}
#define CHECK_OUT_OF_RANGE_CONVERSION(type) \
{ \
std::string s = std::to_string(std::numeric_limits<type>::max()); \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK(t.has_value()); \
for (auto& i : s) { \
if (i != '9' && i != '.') { \
i = '9'; \
break; \
} \
} \
t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK_FALSE(t.has_value()); \
} \
{ \
std::string s = std::to_string(std::numeric_limits<type>::min()); \
auto t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK(t.has_value()); \
for (auto& i : s) { \
if (std::is_signed_v<type> && i != '9' && i != '.') { \
i = '9'; \
break; \
} else if (std::is_unsigned_v<type>) { \
s = "-1"; \
break; \
} \
} \
t = ss::to_num<type>(s.c_str(), s.c_str() + s.size()); \
CHECK_FALSE(t.has_value()); \
TEST_CASE_TEMPLATE(
"extract test functions for numbers with out of range inputs", T, short, us,
int, ui, long, ul, ll, ull, ss::uint8) {
{
std::string s = std::to_string(std::numeric_limits<T>::max());
auto t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
CHECK(t.has_value());
for (auto& i : s) {
if (i != '9' && i != '.') {
i = '9';
break;
}
}
t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
CHECK_FALSE(t.has_value());
}
{
std::string s = std::to_string(std::numeric_limits<T>::min());
auto t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
CHECK(t.has_value());
for (auto& i : s) {
if (std::is_signed_v<T> && i != '9' && i != '.') {
i = '9';
break;
} else if (std::is_unsigned_v<T>) {
s = "-1";
break;
}
}
t = ss::to_num<T>(s.c_str(), s.c_str() + s.size());
CHECK_FALSE(t.has_value());
}
TEST_CASE("extract test functions for numbers with out of range inputs") {
CHECK_OUT_OF_RANGE_CONVERSION(short);
CHECK_OUT_OF_RANGE_CONVERSION(us);
CHECK_OUT_OF_RANGE_CONVERSION(int);
CHECK_OUT_OF_RANGE_CONVERSION(ui);
CHECK_OUT_OF_RANGE_CONVERSION(long);
CHECK_OUT_OF_RANGE_CONVERSION(ul);
CHECK_OUT_OF_RANGE_CONVERSION(ll);
CHECK_OUT_OF_RANGE_CONVERSION(ull);
}
TEST_CASE("extract test functions for boolean values") {
@ -142,12 +149,12 @@ TEST_CASE("extract test functions for char values") {
}
}
TEST_CASE("extract test functions for std::optional") {
for (const auto& [i, s] :
{std::pair<std::optional<int>, std::string>{1, "1"},
TEST_CASE_TEMPLATE("extract test functions for std::optional", T, int,
ss::int8) {
for (const auto& [i, s] : {std::pair<std::optional<T>, std::string>{1, "1"},
{69, "69"},
{-4, "-4"}}) {
std::optional<int> v;
std::optional<T> v;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
REQUIRE(v.has_value());
CHECK_EQ(*v, i);
@ -164,7 +171,7 @@ TEST_CASE("extract test functions for std::optional") {
}
for (const std::string s : {"aa", "xxx", ""}) {
std::optional<int> v;
std::optional<T> v;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), v));
CHECK_FALSE(v.has_value());
}
@ -176,56 +183,57 @@ TEST_CASE("extract test functions for std::optional") {
}
}
TEST_CASE("extract test functions for std::variant") {
TEST_CASE_TEMPLATE("extract test functions for std::variant", T, int,
ss::uint8) {
{
std::string s = "22";
{
std::variant<int, double, std::string> var;
std::variant<T, double, std::string> 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);
REQUIRE_VARIANT(var, 22, T);
}
{
std::variant<double, int, std::string> var;
std::variant<double, T, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22, double);
}
{
std::variant<std::string, double, int> var;
std::variant<std::string, double, T> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "22", std::string);
}
{
std::variant<int> var;
std::variant<T> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, 22, int);
REQUIRE_VARIANT(var, 22, T);
}
}
{
std::string s = "22.2";
{
std::variant<int, double, std::string> var;
std::variant<T, double, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22.2, double);
}
{
std::variant<double, int, std::string> var;
std::variant<double, T, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, std::string);
REQUIRE_VARIANT(var, 22.2, double);
}
{
std::variant<std::string, double, int> var;
std::variant<std::string, double, T> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "22.2", std::string);
}
@ -233,45 +241,45 @@ TEST_CASE("extract test functions for std::variant") {
{
std::string s = "2.2.2";
{
std::variant<int, double, std::string> var;
std::variant<T, double, std::string> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<double, std::string, int> var;
std::variant<double, std::string, T> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<std::string, double, int> var;
std::variant<std::string, double, T> var;
REQUIRE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
CHECK_NOT_VARIANT(var, double);
REQUIRE_VARIANT(var, "2.2.2", std::string);
}
{
std::variant<int, double> var;
std::variant<T, double> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, int{}, int);
REQUIRE_VARIANT(var, T{}, T);
CHECK_NOT_VARIANT(var, double);
}
{
std::variant<double, int> var;
std::variant<double, T> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, double{}, double);
CHECK_NOT_VARIANT(var, int);
CHECK_NOT_VARIANT(var, T);
}
{
std::variant<int> var;
std::variant<T> var;
REQUIRE_FALSE(ss::extract(s.c_str(), s.c_str() + s.size(), var));
REQUIRE_VARIANT(var, int{}, int);
REQUIRE_VARIANT(var, T{}, T);
}
}
}