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 #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> template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num( std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) { 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; 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 // extract
//////////////// ////////////////
@ -103,7 +147,8 @@ struct unsupported_type {
template <typename T> template <typename T>
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<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::optional, T> &&
!is_instance_of_v<std::variant, T>, !is_instance_of_v<std::variant, T> &&
!is_instance_of_v<numeric_wrapper, T>,
bool> bool>
extract(const char*, const char*, T&) { extract(const char*, const char*, T&) {
static_assert(error::unsupported_type<T>::value, static_assert(error::unsupported_type<T>::value,
@ -112,7 +157,9 @@ extract(const char*, const char*, T&) {
} }
template <typename 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) { extract(const char* begin, const char* end, T& value) {
auto optional_value = to_num<T>(begin, end); auto optional_value = to_num<T>(begin, end);
if (!optional_value) { 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 #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> template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num( std::enable_if_t<std::is_integral_v<T>, std::optional<T>> to_num(
const char* const begin, const char* const end) { 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; 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 // extract
//////////////// ////////////////
@ -1557,7 +1601,8 @@ struct unsupported_type {
template <typename T> template <typename T>
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<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::optional, T> &&
!is_instance_of_v<std::variant, T>, !is_instance_of_v<std::variant, T> &&
!is_instance_of_v<numeric_wrapper, T>,
bool> bool>
extract(const char*, const char*, T&) { extract(const char*, const char*, T&) {
static_assert(error::unsupported_type<T>::value, static_assert(error::unsupported_type<T>::value,
@ -1566,7 +1611,9 @@ extract(const char*, const char*, T&) {
} }
template <typename 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) { extract(const char* begin, const char* end, T& value) {
auto optional_value = to_num<T>(begin, end); auto optional_value = to_num<T>(begin, end);
if (!optional_value) { 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; ss::converter c;
{ {
auto tup = c.convert<int>("5"); auto tup = c.convert<T>("5");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} }
{ {
auto tup = c.convert<int, void>("5,junk"); auto tup = c.convert<T, void>("5,junk");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} }
{ {
auto tup = c.convert<void, int>("junk,5"); auto tup = c.convert<void, T>("junk,5");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} }
{ {
auto tup = 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(c.valid());
REQUIRE(tup.has_value()); REQUIRE(tup.has_value());
CHECK_EQ(tup, 5); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
} }
{ {
auto tup = auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(std::get<0>(tup).has_value()); REQUIRE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
} }
{ {
auto tup = 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(c.valid());
REQUIRE_FALSE(std::get<0>(tup).has_value()); 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 = 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(c.valid());
REQUIRE(std::holds_alternative<int>(std::get<0>(tup))); REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6)); CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5}, 6.6));
} }
{ {
auto tup = 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(c.valid());
REQUIRE(std::holds_alternative<double>(std::get<0>(tup))); 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, 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; ss::converter<ss::throw_on_error> c;
try { try {
auto tup = c.convert<int>("5"); auto tup = c.convert<T>("5");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -152,7 +151,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { try {
auto tup = c.convert<int, void>("5,junk"); auto tup = c.convert<T, void>("5,junk");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -160,7 +159,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { try {
auto tup = c.convert<void, int>("junk,5"); auto tup = c.convert<void, T>("junk,5");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -168,7 +167,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -176,7 +175,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { try {
auto tup = c.convert<void, int, void>("junk 5 junk", " "); auto tup = c.convert<void, T, void>("junk 5 junk", " ");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -184,7 +183,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -193,7 +192,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
try { try {
auto tup = 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(c.valid());
REQUIRE(tup.has_value()); REQUIRE(tup.has_value());
CHECK_EQ(tup, 5); CHECK_EQ(tup, 5);
@ -202,7 +201,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -210,7 +209,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -218,7 +217,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
} catch (ss::exception& e) { } catch (ss::exception& e) {
@ -226,8 +225,7 @@ TEST_CASE("converter test valid conversions with exceptions") {
} }
try { try {
auto tup = auto tup = c.convert<void, std::optional<T>, double>("junk;5;6.6", ";");
c.convert<void, std::optional<int>, double>("junk;5;6.6", ";");
REQUIRE(c.valid()); REQUIRE(c.valid());
REQUIRE(std::get<0>(tup).has_value()); REQUIRE(std::get<0>(tup).has_value());
CHECK_EQ(tup, std::make_tuple(5, 6.6)); CHECK_EQ(tup, std::make_tuple(5, 6.6));
@ -237,32 +235,31 @@ TEST_CASE("converter test valid conversions with exceptions") {
try { try {
auto tup = 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(c.valid());
REQUIRE_FALSE(std::get<0>(tup).has_value()); 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) { } catch (ss::exception& e) {
FAIL(std::string{e.what()}); FAIL(std::string{e.what()});
} }
try { try {
auto tup = 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(c.valid());
REQUIRE(std::holds_alternative<int>(std::get<0>(tup))); REQUIRE(std::holds_alternative<T>(std::get<0>(tup)));
CHECK_EQ(tup, std::make_tuple(std::variant<int, double>{5}, 6.6)); CHECK_EQ(tup, std::make_tuple(std::variant<T, double>{5}, 6.6));
} catch (ss::exception& e) { } catch (ss::exception& e) {
FAIL(std::string{e.what()}); FAIL(std::string{e.what()});
} }
try { try {
auto tup = 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(c.valid());
REQUIRE(std::holds_alternative<double>(std::get<0>(tup))); 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) { } catch (ss::exception& e) {
FAIL(std::string{e.what()}); 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; ss::converter c;
c.convert<int>(""); c.convert<T>("");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int>("1", ""); c.convert<T>("1", "");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int>("10", ""); c.convert<T>("10", "");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int, void>(""); c.convert<T, void>("");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int, void>(",junk"); c.convert<T, void>(",junk");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<void, int>("junk,"); c.convert<void, T>("junk,");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int>("x"); c.convert<T>("x");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int, void>("x"); c.convert<T, void>("x");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<int, void>("x,junk"); c.convert<T, void>("x,junk");
REQUIRE_FALSE(c.valid()); REQUIRE_FALSE(c.valid());
c.convert<void, int>("junk,x"); c.convert<void, T>("junk,x");
REQUIRE_FALSE(c.valid()); 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()); 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; ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<int>("")); REQUIRE_EXCEPTION(c.convert<T>(""));
REQUIRE_EXCEPTION(c.convert<int>("1", "")); REQUIRE_EXCEPTION(c.convert<T>("1", ""));
REQUIRE_EXCEPTION(c.convert<int>("10", "")); REQUIRE_EXCEPTION(c.convert<T>("10", ""));
REQUIRE_EXCEPTION(c.convert<int, void>("")); REQUIRE_EXCEPTION(c.convert<T, void>(""));
REQUIRE_EXCEPTION(c.convert<int, void>(",junk")); REQUIRE_EXCEPTION(c.convert<T, void>(",junk"));
REQUIRE_EXCEPTION(c.convert<void, int>("junk,")); REQUIRE_EXCEPTION(c.convert<void, T>("junk,"));
REQUIRE_EXCEPTION(c.convert<int>("x")); REQUIRE_EXCEPTION(c.convert<T>("x"));
REQUIRE_EXCEPTION(c.convert<int, void>("x")); REQUIRE_EXCEPTION(c.convert<T, void>("x"));
REQUIRE_EXCEPTION(c.convert<int, void>("x,junk")); REQUIRE_EXCEPTION(c.convert<T, void>("x,junk"));
REQUIRE_EXCEPTION(c.convert<void, int>("junk,x")); REQUIRE_EXCEPTION(c.convert<void, T>("junk,x"));
REQUIRE_EXCEPTION( 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; ss::converter c;
c.convert<ss::ax<int, 0>>("0"); c.convert<ss::ax<T, 0>>("0");
REQUIRE_FALSE(c.valid()); 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()); 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()); 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()); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 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");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('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");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(3, 'c')); 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; ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0>>("0")); REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0>>("0"));
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 0, 1, 2>>("1")); REQUIRE_EXCEPTION(c.convert<ss::ax<T, 0, 1, 2>>("1"));
REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1")); REQUIRE_EXCEPTION(c.convert<void, char, ss::ax<T, 0, 1, 2>>("junk,c,1"));
REQUIRE_EXCEPTION(c.convert<ss::ax<int, 1>, char>("1,c")); REQUIRE_EXCEPTION(c.convert<ss::ax<T, 1>, char>("1,c"));
try { try {
{ {
int tup = c.convert<ss::ax<int, 1>>("3"); T tup = c.convert<ss::ax<T, 1>>("3");
CHECK_EQ(tup, 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)); 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')); CHECK_EQ(tup, std::make_tuple(3, 'c'));
} }
} catch (ss::exception& e) { } 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; ss::converter c;
c.convert<ss::ir<int, 0, 2>>("3"); c.convert<ss::ir<T, 0, 2>>("3");
REQUIRE_FALSE(c.valid()); 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()); 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()); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 3); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 2); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('c', 1)); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(1, 'c')); 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; ss::converter<ss::throw_on_error> c;
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 0, 2>>("3")); REQUIRE_EXCEPTION(c.convert<ss::ir<T, 0, 2>>("3"));
REQUIRE_EXCEPTION(c.convert<char, ss::ir<int, 4, 69>>("c,3")); REQUIRE_EXCEPTION(c.convert<char, ss::ir<T, 4, 69>>("c,3"));
REQUIRE_EXCEPTION(c.convert<ss::ir<int, 1, 2>, char>("3,c")); REQUIRE_EXCEPTION(c.convert<ss::ir<T, 1, 2>, char>("3,c"));
try { try {
{ {
auto tup = c.convert<ss::ir<int, 1, 5>>("3"); auto tup = c.convert<ss::ir<T, 1, 5>>("3");
REQUIRE(c.valid()); REQUIRE(c.valid());
CHECK_EQ(tup, 3); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, 2); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple('c', 1)); 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()); REQUIRE(c.valid());
CHECK_EQ(tup, std::make_tuple(1, 'c')); 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\")"))); buff(R"(just,some,2,"strings\")")));
CHECK(c.unterminated_quote()); CHECK(c.unterminated_quote());
} }

View File

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