move values inside composite when possible, write unit tests for composite

This commit is contained in:
ado 2020-12-26 00:36:08 +01:00
parent 616316d2a7
commit 371da072be
3 changed files with 235 additions and 28 deletions

View File

@ -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:

View File

@ -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>

View File

@ -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&) {});
}