From 371da072be5b44ed43c23bcd37c23ad517b26294 Mon Sep 17 00:00:00 2001 From: ado Date: Sat, 26 Dec 2020 00:36:08 +0100 Subject: [PATCH] move values inside composite when possible, write unit tests for composite --- include/ss/parser.hpp | 15 +-- include/ss/type_traits.hpp | 3 +- test/test_parser.cpp | 245 ++++++++++++++++++++++++++++++++++--- 3 files changed, 235 insertions(+), 28 deletions(-) diff --git a/include/ss/parser.hpp b/include/ss/parser.hpp index 9e65191..09155f2 100644 --- a/include/ss/parser.hpp +++ b/include/ss/parser.hpp @@ -86,8 +86,8 @@ class parser { template class composite { public: - composite(std::tuple values, parser& parser) - : values_{values}, parser_{parser} { + composite(std::tuple&& 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(); if constexpr (!std::is_same_v< U, decltype(tuple_output)>) { - new_value = to_object(tuple_output); + new_value = to_object( + 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)); @@ -181,10 +182,10 @@ class parser { std::optional> value; auto new_value = get_next(); if (valid()) { - value = new_value; + value = std::move(new_value); try_invoke(*value, std::forward(fun)); } - return {value, *this}; + return {std::move(value), *this}; }; private: diff --git a/include/ss/type_traits.hpp b/include/ss/type_traits.hpp index 34ab136..70dddc1 100644 --- a/include/ss/type_traits.hpp +++ b/include/ss/type_traits.hpp @@ -328,8 +328,7 @@ struct is_instance_of, Template> { template S to_object(std::index_sequence, Tup&& tup) { - using std::get; - return {get(std::forward(tup))...}; + return {std::get(std::forward(tup))...}; } template diff --git a/test/test_parser.cpp b/test/test_parser.cpp index 4692aec..7e1ba61 100644 --- a/test/test_parser.cpp +++ b/test/test_parser.cpp @@ -72,7 +72,8 @@ TEST_CASE("testing parser") { p.ignore_next(); while (!p.eof()) { - auto a = p.get_next(); + using tup = std::tuple; + auto a = p.get_next(); i.emplace_back(ss::to_object(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 i; + + while (!p.eof()) { + using tup = std::tuple; + i.push_back(p.get_object()); + } + + CHECK(std::equal(i.begin(), i.end(), data.begin())); + } + { ss::parser p{f.name, ","}; std::vector i; @@ -150,8 +163,186 @@ TEST_CASE("testing parser") { } } +using test_tuple = std::tuple; +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(fail) + .or_else(fail) + .or_else( + [](auto&& data) { CHECK(data == expectedData); }) + .on_error(fail_if_error) + .or_else(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( + [](auto& i1, auto i2, double d) { + CHECK(std::tie(i1, i2, d) == expectedData); + }) + .on_error(fail_if_error) + .or_else_object(fail) + .on_error(fail_if_error) + .or_else(fail) + .on_error(fail_if_error) + .or_else(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(fail) + .on_error(expect_error) + .or_else_object(fail) + .or_else(fail) + .or_else(fail) + .or_else(fail) + .values(); + + REQUIRE(!p.valid()); + REQUIRE(!d1); + REQUIRE(!d2); + REQUIRE(!d3); + REQUIRE(!d4); + REQUIRE(!d5); + } + + { + REQUIRE(!p.eof()); + + auto [d1, d2] = + p.try_next([](auto& i, auto& d) { + REQUIRE(std::tie(i, d) == std::tuple{10, 11.1}); + }) + .or_else([](auto&, auto&) { FAIL(""); }) + .values(); + + REQUIRE(p.valid()); + REQUIRE(d1); + REQUIRE(!d2); + } + + { + REQUIRE(!p.eof()); + + auto [d1, d2] = + p.try_next([](auto&, auto&) { FAIL(""); }) + .or_else(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(fail) + .or_else_object() + .or_else(expect_test_struct) + .or_else(fail) + .or_else>(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>() + .on_error(fail_if_error) + .or_else>(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>() + .on_error(fail_if_error) + .or_else>(fail) + .on_error(fail_if_error) + .values(); + + REQUIRE(p.valid()); + REQUIRE(d1); + REQUIRE(!d2); + CHECK(*d1 == + std::tuple{11, std::variant{"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; + { + std::ofstream out{f.name}; + out << "x" << std::endl; + } ss::parser p{f.name, ","}; auto x = p.get_next(); - 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(); - 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(); - CHECK(copy_called == 0); + auto x = p.get_object(); 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(); - CHECK(copy_called == 0); + auto x = p.get_next(); 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() + .or_else([](auto&&) {}) + .or_else([](auto&) {}) + .or_else([](auto&&) {}) + .or_else_object([](auto&&) {}) + .or_else>( + [](auto&, auto&, auto&) {}); +}