mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 04:55:20 +01:00
move values inside composite when possible, write unit tests for composite
This commit is contained in:
parent
616316d2a7
commit
371da072be
@ -86,8 +86,8 @@ class parser {
|
||||
template <typename... Ts>
|
||||
class composite {
|
||||
public:
|
||||
composite(std::tuple<Ts...> values, parser& parser)
|
||||
: values_{values}, parser_{parser} {
|
||||
composite(std::tuple<Ts...>&& values, parser& parser)
|
||||
: values_{std::move(values)}, parser_{parser} {
|
||||
}
|
||||
|
||||
// tries to convert the same line with a different output type
|
||||
@ -145,12 +145,13 @@ class parser {
|
||||
auto tuple_output = try_same<Us...>();
|
||||
if constexpr (!std::is_same_v<
|
||||
U, decltype(tuple_output)>) {
|
||||
new_value = to_object<U>(tuple_output);
|
||||
new_value = to_object<U>(
|
||||
std::move(tuple_output));
|
||||
} else {
|
||||
new_value = tuple_output;
|
||||
new_value = std::move(tuple_output);
|
||||
}
|
||||
if (parser_.valid()) {
|
||||
value = new_value;
|
||||
value = std::move(new_value);
|
||||
parser_.try_invoke(*value,
|
||||
std::forward<Fun>(
|
||||
fun));
|
||||
@ -181,10 +182,10 @@ class parser {
|
||||
std::optional<no_void_validator_tup_t<Ts...>> value;
|
||||
auto new_value = get_next<Ts...>();
|
||||
if (valid()) {
|
||||
value = new_value;
|
||||
value = std::move(new_value);
|
||||
try_invoke(*value, std::forward<Fun>(fun));
|
||||
}
|
||||
return {value, *this};
|
||||
return {std::move(value), *this};
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -328,8 +328,7 @@ struct is_instance_of<Template<Ts...>, Template> {
|
||||
|
||||
template <class S, std::size_t... Is, class Tup>
|
||||
S to_object(std::index_sequence<Is...>, Tup&& tup) {
|
||||
using std::get;
|
||||
return {get<Is>(std::forward<Tup>(tup))...};
|
||||
return {std::get<Is>(std::forward<Tup>(tup))...};
|
||||
}
|
||||
|
||||
template <class S, class Tup>
|
||||
|
@ -72,7 +72,8 @@ TEST_CASE("testing parser") {
|
||||
|
||||
p.ignore_next();
|
||||
while (!p.eof()) {
|
||||
auto a = p.get_next<int, double, std::string>();
|
||||
using tup = std::tuple<int, double, std::string>;
|
||||
auto a = p.get_next<tup>();
|
||||
i.emplace_back(ss::to_object<X>(a));
|
||||
}
|
||||
|
||||
@ -91,6 +92,18 @@ TEST_CASE("testing parser") {
|
||||
CHECK(std::equal(i.begin(), i.end(), data.begin()));
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser p{f.name, ","};
|
||||
std::vector<X> i;
|
||||
|
||||
while (!p.eof()) {
|
||||
using tup = std::tuple<int, double, std::string>;
|
||||
i.push_back(p.get_object<X, tup>());
|
||||
}
|
||||
|
||||
CHECK(std::equal(i.begin(), i.end(), data.begin()));
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser p{f.name, ","};
|
||||
std::vector<X> i;
|
||||
@ -150,8 +163,186 @@ TEST_CASE("testing parser") {
|
||||
}
|
||||
}
|
||||
|
||||
using test_tuple = std::tuple<double, char, double>;
|
||||
struct test_struct {
|
||||
int i;
|
||||
double d;
|
||||
char c;
|
||||
auto tied() {
|
||||
return std::tie(i, d, c);
|
||||
}
|
||||
};
|
||||
|
||||
void expect_test_struct(const test_struct&) {
|
||||
}
|
||||
|
||||
// various scenarios
|
||||
TEST_CASE("testing composite conversion") {
|
||||
unique_file_name f;
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
for (auto& i : {"10,a,11.1", "10,20,11.1", "junk", "10,11.1",
|
||||
"1,11.1,a", "junk", "10,junk", "11,junk"}) {
|
||||
out << i << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
ss::parser p{f.name, ","};
|
||||
auto fail = [] { FAIL(""); };
|
||||
auto fail_if_error = [](auto& error) { CHECK(error.empty()); };
|
||||
auto expect_error = [](auto error) { CHECK(!error.empty()); };
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
{
|
||||
constexpr static auto expectedData = std::tuple{10, 'a', 11.1};
|
||||
|
||||
auto [d1, d2, d3, d4] =
|
||||
p.try_next<int, int, double>(fail)
|
||||
.or_else<test_struct>(fail)
|
||||
.or_else<int, char, double>(
|
||||
[](auto&& data) { CHECK(data == expectedData); })
|
||||
.on_error(fail_if_error)
|
||||
.or_else<test_tuple>(fail)
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(!d1);
|
||||
REQUIRE(!d2);
|
||||
REQUIRE(d3);
|
||||
REQUIRE(!d4);
|
||||
CHECK(*d3 == expectedData);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
constexpr static auto expectedData = std::tuple{10, 20, 11.1};
|
||||
|
||||
auto [d1, d2, d3, d4] =
|
||||
p.try_next<int, int, double>(
|
||||
[](auto& i1, auto i2, double d) {
|
||||
CHECK(std::tie(i1, i2, d) == expectedData);
|
||||
})
|
||||
.on_error(fail_if_error)
|
||||
.or_else_object<test_struct, int, double, char>(fail)
|
||||
.on_error(fail_if_error)
|
||||
.or_else<test_tuple>(fail)
|
||||
.on_error(fail_if_error)
|
||||
.or_else<int, char, double>(fail)
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(d1);
|
||||
REQUIRE(!d2);
|
||||
REQUIRE(!d3);
|
||||
REQUIRE(!d4);
|
||||
CHECK(*d1 == expectedData);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2, d3, d4, d5] =
|
||||
p.try_next<int, int, double>(fail)
|
||||
.on_error(expect_error)
|
||||
.or_else_object<test_struct, int, double, char>(fail)
|
||||
.or_else<test_struct>(fail)
|
||||
.or_else<test_tuple>(fail)
|
||||
.or_else<int, char, double>(fail)
|
||||
.values();
|
||||
|
||||
REQUIRE(!p.valid());
|
||||
REQUIRE(!d1);
|
||||
REQUIRE(!d2);
|
||||
REQUIRE(!d3);
|
||||
REQUIRE(!d4);
|
||||
REQUIRE(!d5);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2] =
|
||||
p.try_next<int, double>([](auto& i, auto& d) {
|
||||
REQUIRE(std::tie(i, d) == std::tuple{10, 11.1});
|
||||
})
|
||||
.or_else<int, double>([](auto&, auto&) { FAIL(""); })
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(d1);
|
||||
REQUIRE(!d2);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2] =
|
||||
p.try_next<int, double>([](auto&, auto&) { FAIL(""); })
|
||||
.or_else<test_struct>(expect_test_struct)
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(!d1);
|
||||
REQUIRE(d2);
|
||||
CHECK(d2->tied() == std::tuple{1, 11.1, 'a'});
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2, d3, d4, d5] =
|
||||
p.try_next<int, int, double>(fail)
|
||||
.or_else_object<test_struct, int, double, char>()
|
||||
.or_else<test_struct>(expect_test_struct)
|
||||
.or_else<test_tuple>(fail)
|
||||
.or_else<std::tuple<int, double>>(fail)
|
||||
.on_error(expect_error)
|
||||
.values();
|
||||
|
||||
REQUIRE(!p.valid());
|
||||
REQUIRE(!d1);
|
||||
REQUIRE(!d2);
|
||||
REQUIRE(!d3);
|
||||
REQUIRE(!d4);
|
||||
REQUIRE(!d5);
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2] = p.try_next<int, std::optional<int>>()
|
||||
.on_error(fail_if_error)
|
||||
.or_else<std::tuple<int, std::string>>(fail)
|
||||
.on_error(fail_if_error)
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(d1);
|
||||
REQUIRE(!d2);
|
||||
CHECK(*d1 == std::tuple{10, std::nullopt});
|
||||
}
|
||||
|
||||
{
|
||||
REQUIRE(!p.eof());
|
||||
|
||||
auto [d1, d2] =
|
||||
p.try_next<int, std::variant<int, std::string>>()
|
||||
.on_error(fail_if_error)
|
||||
.or_else<std::tuple<int, std::string>>(fail)
|
||||
.on_error(fail_if_error)
|
||||
.values();
|
||||
|
||||
REQUIRE(p.valid());
|
||||
REQUIRE(d1);
|
||||
REQUIRE(!d2);
|
||||
CHECK(*d1 ==
|
||||
std::tuple{11, std::variant<int, std::string>{"junk"}});
|
||||
}
|
||||
}
|
||||
|
||||
size_t move_called = 0;
|
||||
size_t copy_called = 0;
|
||||
|
||||
struct my_string {
|
||||
char* data{nullptr};
|
||||
@ -162,14 +353,20 @@ struct my_string {
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
my_string(const my_string&) {
|
||||
copy_called++;
|
||||
}
|
||||
// make sure no object is copied
|
||||
my_string(const my_string&) = delete;
|
||||
my_string& operator=(const my_string&) = delete;
|
||||
|
||||
my_string(my_string&& other) : data{other.data} {
|
||||
move_called++;
|
||||
other.data = nullptr;
|
||||
}
|
||||
|
||||
my_string& operator=(my_string&& other) {
|
||||
move_called++;
|
||||
data = other.data;
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
@ -181,7 +378,7 @@ inline bool ss::extract(const char* begin, const char* end, my_string& s) {
|
||||
return true;
|
||||
}
|
||||
|
||||
struct Y {
|
||||
struct xyz {
|
||||
my_string x;
|
||||
my_string y;
|
||||
my_string z;
|
||||
@ -195,15 +392,16 @@ TEST_CASE("testing the moving of parsed values") {
|
||||
|
||||
{
|
||||
unique_file_name f;
|
||||
{
|
||||
std::ofstream out{f.name};
|
||||
out << "x" << std::endl;
|
||||
}
|
||||
|
||||
ss::parser p{f.name, ","};
|
||||
auto x = p.get_next<my_string>();
|
||||
CHECK(copy_called == 0);
|
||||
CHECK(move_called < 3);
|
||||
move_called_one_col = move_called;
|
||||
move_called = copy_called = 0;
|
||||
move_called = 0;
|
||||
}
|
||||
|
||||
unique_file_name f;
|
||||
@ -216,25 +414,34 @@ TEST_CASE("testing the moving of parsed values") {
|
||||
|
||||
ss::parser p{f.name, ","};
|
||||
auto x = p.get_next<my_string, my_string, my_string>();
|
||||
CHECK(copy_called == 0);
|
||||
CHECK(move_called <= 3 * move_called_one_col);
|
||||
move_called = copy_called = 0;
|
||||
move_called = 0;
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser p{f.name, ","};
|
||||
auto x = p.get_object<Y, my_string, my_string, my_string>();
|
||||
CHECK(copy_called == 0);
|
||||
auto x = p.get_object<xyz, my_string, my_string, my_string>();
|
||||
CHECK(move_called <= 6 * move_called_one_col);
|
||||
std::cout << move_called << std::endl;
|
||||
move_called = copy_called = 0;
|
||||
move_called = 0;
|
||||
}
|
||||
|
||||
{
|
||||
ss::parser p{f.name, ","};
|
||||
auto x = p.get_next<Y>();
|
||||
CHECK(copy_called == 0);
|
||||
auto x = p.get_next<xyz>();
|
||||
CHECK(move_called <= 6 * move_called_one_col);
|
||||
move_called = copy_called = 0;
|
||||
move_called = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("testing the moving of parsed composite values") {
|
||||
// to compile is enough
|
||||
return;
|
||||
ss::parser* p;
|
||||
p->try_next<my_string, my_string, my_string>()
|
||||
.or_else<my_string, my_string, my_string, my_string>([](auto&&) {})
|
||||
.or_else<my_string>([](auto&) {})
|
||||
.or_else<xyz>([](auto&&) {})
|
||||
.or_else_object<xyz, my_string, my_string, my_string>([](auto&&) {})
|
||||
.or_else<std::tuple<my_string, my_string, my_string>>(
|
||||
[](auto&, auto&, auto&) {});
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user