/******************************************************************************* * This file is part of the "https://github.com/blackmatov/meta.hpp" * For conditions of distribution and use, see copyright notice in LICENSE.md * Copyright (C) 2021-2023, by Matvey Cherevko (blackmatov@gmail.com) ******************************************************************************/ #include #include #include namespace { struct ivec2 { int x{}; int y{}; ivec2() = delete; explicit ivec2(int nv): x{nv}, y{nv} {} ivec2(int nx, int ny): x{nx}, y{ny} {} ivec2(ivec2&& other) noexcept : x{other.x} , y{other.y} { other.x = 0; other.y = 0; ++move_constructor_counter; } ivec2(const ivec2& other) noexcept : x{other.x} , y{other.y} { ++copy_constructor_counter; } ivec2& add(const ivec2& other) { x += other.x; y += other.y; return *this; } ivec2& operator=(ivec2&& other) = delete; ivec2& operator=(const ivec2& other) = delete; public: inline static int move_constructor_counter{}; inline static int copy_constructor_counter{}; }; struct ivec3 { int x{}; int y{}; int z{}; ivec3() = delete; explicit ivec3(int nv): x{nv}, y{nv}, z{nv} {} ivec3(int nx, int ny, int nz): x{nx}, y{ny}, z{nz} {} }; struct ivec2_big { int x{}; int y{}; int dummy[42]{}; ivec2_big() = delete; explicit ivec2_big(int nv): x{nv}, y{nv} {} ivec2_big(int nx, int ny): x{nx}, y{ny} {} ivec2_big(ivec2_big&& other) noexcept : x{other.x} , y{other.y} { other.x = 0; other.y = 0; ++move_constructor_counter; } ivec2_big(const ivec2_big& other) noexcept : x{other.x} , y{other.y} { ++copy_constructor_counter; } ivec2_big& add(const ivec2_big& other) { x += other.x; y += other.y; return *this; } ivec2_big& operator=(ivec2_big&& other) = delete; ivec2_big& operator=(const ivec2_big& other) = delete; public: inline static int move_constructor_counter{}; inline static int copy_constructor_counter{}; }; ivec2 iadd2(ivec2 l, ivec2 r) { return {l.x + r.x, l.y + r.y}; } bool operator==(const ivec2& l, const ivec2& r) noexcept { return l.x == r.x && l.y == r.y; } bool operator==(const ivec2_big& l, const ivec2_big& r) noexcept { return l.x == r.x && l.y == r.y; } } TEST_CASE("meta/meta_utilities/value/_") { namespace meta = meta_hpp; meta::class_() .constructor_() .constructor_() .constructor_() .constructor_() .member_("x", &ivec2::x) .member_("y", &ivec2::y); meta::class_() .constructor_() .constructor_() .constructor_() .constructor_() .member_("x", &ivec3::x) .member_("y", &ivec3::y) .member_("z", &ivec3::z); } TEST_CASE("meta/meta_utilities/value") { namespace meta = meta_hpp; using namespace std::string_literals; ivec2::move_constructor_counter = 0; ivec2::copy_constructor_counter = 0; ivec2_big::move_constructor_counter = 0; ivec2_big::copy_constructor_counter = 0; SUBCASE("cast types") { static_assert(std::is_same_v< decltype(std::declval().get_as()), ivec2&>); static_assert(std::is_same_v< decltype(std::declval().get_as()), ivec2>); static_assert(std::is_same_v< decltype(std::declval().get_as()), const ivec2&>); static_assert(std::is_same_v< decltype(std::declval().get_as()), const ivec2&>); } SUBCASE("ivec2{}") { { meta::uvalue val{}; CHECK(!val); CHECK_FALSE(val); CHECK_FALSE(val.is_valid()); CHECK(val.get_data() == nullptr); CHECK(std::as_const(val).get_data() == nullptr); CHECK(std::as_const(val).get_cdata() == nullptr); CHECK_FALSE(*val); CHECK_FALSE(val[0]); CHECK_FALSE(val.try_get_as()); CHECK_FALSE(std::as_const(val).try_get_as()); CHECK_THROWS(std::ignore = val.get_as()); CHECK_THROWS(std::ignore = std::as_const(val).get_as()); CHECK_THROWS(std::ignore = std::move(val).get_as()); CHECK_THROWS(std::ignore = std::move(std::as_const(val)).get_as()); } CHECK(meta::uvalue{}.get_type() == meta::resolve_type()); } SUBCASE("ivec2&") { ivec2 v{1,2}; ivec2& vr = v; meta::uvalue val{vr}; CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 1); CHECK(val.get_type() == meta::resolve_type()); CHECK(*static_cast(val.get_data()) == vr); CHECK(*static_cast(val.get_cdata()) == vr); CHECK(*static_cast(std::as_const(val).get_data()) == vr); CHECK(*static_cast(std::as_const(val).get_cdata()) == vr); CHECK(val.get_as() == ivec2{1,2}); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); { meta::uvalue val_copy{val}; CHECK(std::move(val_copy).get_as() == ivec2{1,2}); } { meta::uvalue val_copy{val}; CHECK(std::move(std::as_const(val_copy)).get_as() == ivec2{1,2}); } CHECK_THROWS(std::ignore = val.get_as()); CHECK_THROWS(std::ignore = std::as_const(val).get_as()); CHECK_THROWS(std::ignore = std::move(val).get_as()); CHECK_THROWS(std::ignore = std::move(std::as_const(val)).get_as()); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); CHECK_FALSE(val.try_get_as()); CHECK_FALSE(std::as_const(val).try_get_as()); } SUBCASE("const ivec2&") { const ivec2 v{1,2}; const ivec2& vr = v; meta::uvalue val{vr}; CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 1); CHECK(val.get_type() == meta::resolve_type()); CHECK(*static_cast(val.get_data()) == vr); CHECK(*static_cast(val.get_cdata()) == vr); CHECK(*static_cast(std::as_const(val).get_data()) == vr); CHECK(*static_cast(std::as_const(val).get_cdata()) == vr); CHECK(val.get_as() == ivec2{1,2}); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); { meta::uvalue val_copy{val}; CHECK(std::move(val_copy).get_as() == ivec2{1,2}); } { meta::uvalue val_copy{val}; CHECK(std::move(std::as_const(val_copy)).get_as() == ivec2{1,2}); } CHECK_THROWS(std::ignore = val.get_as()); CHECK_THROWS(std::ignore = std::as_const(val).get_as()); CHECK_THROWS(std::ignore = std::move(val).get_as()); CHECK_THROWS(std::ignore = std::move(std::as_const(val)).get_as()); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); CHECK_FALSE(val.try_get_as()); CHECK_FALSE(std::as_const(val).try_get_as()); } SUBCASE("ivec2&&") { ivec2 v{1,2}; meta::uvalue val{std::move(v)}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); CHECK(val.get_type() == meta::resolve_type()); CHECK(val.get_as() == ivec2{1,2}); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); { meta::uvalue val_copy{val}; CHECK(std::move(val_copy).get_as() == ivec2{1,2}); } { meta::uvalue val_copy{val}; CHECK(std::move(std::as_const(val_copy)).get_as() == ivec2{1,2}); } CHECK_THROWS(std::ignore = val.get_as()); CHECK_THROWS(std::ignore = std::as_const(val).get_as()); CHECK_THROWS(std::ignore = std::move(val).get_as()); CHECK_THROWS(std::ignore = std::move(std::as_const(val)).get_as()); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); CHECK_FALSE(val.try_get_as()); CHECK_FALSE(std::as_const(val).try_get_as()); } SUBCASE("const ivec2&&") { const ivec2 v{1,2}; meta::uvalue val{std::move(v)}; CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 1); CHECK(val.get_type() == meta::resolve_type()); CHECK(val.get_as() == ivec2{1,2}); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); { meta::uvalue val_copy{val}; CHECK(std::move(val_copy).get_as() == ivec2{1,2}); } { meta::uvalue val_copy{val}; CHECK(std::move(std::as_const(val_copy)).get_as() == ivec2{1,2}); } CHECK_THROWS(std::ignore = val.get_as()); CHECK_THROWS(std::ignore = std::as_const(val).get_as()); CHECK_THROWS(std::ignore = std::move(val).get_as()); CHECK_THROWS(std::ignore = std::move(std::as_const(val)).get_as()); CHECK(val.get_as() == ivec2{1,2}); CHECK(std::as_const(val).get_as() == ivec2{1,2}); CHECK_FALSE(val.try_get_as()); CHECK_FALSE(std::as_const(val).try_get_as()); } SUBCASE("value(value&&)") { ivec2 v{1,2}; meta::uvalue val_src{std::move(v)}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); meta::uvalue val_dst{std::move(val_src)}; CHECK(val_dst.get_as() == ivec2{1,2}); CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::copy_constructor_counter == 0); } SUBCASE("value(const meta::value&)") { const ivec2 v{1,2}; meta::uvalue val_src{v}; CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 1); meta::uvalue val_dst{val_src}; CHECK(val_dst.get_as() == ivec2{1,2}); CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 2); CHECK(val_src.get_as() == ivec2{1,2}); CHECK(val_src.get_data() != val_dst.get_data()); } SUBCASE("value& operator=(T&&)") { meta::uvalue val{10}; val = 20; CHECK(val.get_as() == 20); val = "hello"s; CHECK(val.get_as() == "hello"s); } SUBCASE("value& operator=(value&&)") { meta::uvalue val_src1{"world"s}; meta::uvalue val_src2{ivec2{1,2}}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); meta::uvalue val_dst{"hello"s}; val_dst = std::move(val_src1); CHECK(val_dst.get_as() == "world"s); CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); val_dst = std::move(val_src2); CHECK(val_dst.get_as() == ivec2{1,2}); CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::copy_constructor_counter == 0); } SUBCASE("value& operator=(const meta::value&)") { meta::uvalue val_src1{"world"s}; meta::uvalue val_src2{ivec2{1,2}}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); meta::uvalue val_dst{"hello"s}; val_dst = val_src1; CHECK(val_dst.get_as() == "world"s); CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); val_dst = val_src2; CHECK(val_dst.get_as() == ivec2{1,2}); CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 1); CHECK(val_src2.get_as() == ivec2{1,2}); CHECK(val_src2.get_data() != val_dst.get_data()); } SUBCASE("swap/0") { meta::uvalue val1{"world"s}; meta::uvalue val2{ivec2{1,2}}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); val1.swap(val2); CHECK(val1.get_as() == ivec2{1,2}); CHECK(val2.get_as() == "world"s); CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::copy_constructor_counter == 0); swap(val1, val2); CHECK(val1.get_as() == "world"s); CHECK(val2.get_as() == ivec2{1,2}); } SUBCASE("swap/1") { meta::uvalue val1{42}; meta::uvalue val2{}; swap(val1, val2); CHECK_FALSE(val1); CHECK(val2.get_as() == 42); swap(val1, val2); CHECK(val1.get_as() == 42); CHECK_FALSE(val2); } SUBCASE("swap/2") { meta::uvalue val1{ivec2{1,2}}; meta::uvalue val2{}; CHECK(ivec2::move_constructor_counter == 1); CHECK(ivec2::copy_constructor_counter == 0); swap(val1, val2); CHECK_FALSE(val1); CHECK(val2.get_as() == ivec2{1,2}); CHECK(ivec2::move_constructor_counter == 2); CHECK(ivec2::copy_constructor_counter == 0); swap(val1, val2); CHECK(val1.get_as() == ivec2{1,2}); CHECK_FALSE(val2); CHECK(ivec2::move_constructor_counter == 3); CHECK(ivec2::copy_constructor_counter == 0); } SUBCASE("swap/3") { meta::uvalue val1{ivec2_big{1,2}}; meta::uvalue val2{}; CHECK(ivec2_big::move_constructor_counter == 1); CHECK(ivec2_big::copy_constructor_counter == 0); swap(val1, val2); CHECK_FALSE(val1); CHECK(val2.get_as() == ivec2_big{1,2}); CHECK(ivec2_big::move_constructor_counter == 1); CHECK(ivec2_big::copy_constructor_counter == 0); swap(val1, val2); CHECK(val1.get_as() == ivec2_big{1,2}); CHECK_FALSE(val2); CHECK(ivec2_big::move_constructor_counter == 1); CHECK(ivec2_big::copy_constructor_counter == 0); } SUBCASE("after_move") { { meta::uvalue val1{42}; meta::uvalue val2{std::move(val1)}; CHECK_FALSE(val1); CHECK(val2.get_as() == 42); val1 = std::move(val2); CHECK_FALSE(val2); CHECK(val1.get_as() == 42); } { meta::uvalue val1{ivec2{1,2}}; meta::uvalue val2{std::move(val1)}; CHECK_FALSE(val1); CHECK(val2); CHECK(val2.get_as() == ivec2{1,2}); val1 = std::move(val2); CHECK_FALSE(val2); CHECK(val1.get_as() == ivec2{1,2}); } { meta::uvalue val1{ivec2_big{1,2}}; meta::uvalue val2{std::move(val1)}; CHECK_FALSE(val1); CHECK(val2); CHECK(val2.get_as() == ivec2_big{1,2}); val1 = std::move(val2); CHECK_FALSE(val2); CHECK(val1.get_as() == ivec2_big{1,2}); } } SUBCASE("unmap") { { const meta::uvalue u{42}; CHECK_FALSE(u.has_unmap_op()); CHECK_FALSE(u.unmap()); } { int i{42}; const meta::uvalue u{std::ref(i)}; CHECK(u.has_unmap_op()); const meta::uvalue v{u.unmap()}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == &i); } { const int i{42}; const meta::uvalue u{std::ref(i)}; CHECK(u.has_unmap_op()); const meta::uvalue v{u.unmap()}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == &i); } { const auto i = std::make_shared(3, 4); const meta::uvalue u{i}; CHECK(u.has_unmap_op()); const meta::uvalue v{u.unmap()}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == i.get()); } { const auto i = std::make_shared(3, 4); const meta::uvalue u{i}; CHECK(u.has_unmap_op()); const meta::uvalue v{u.unmap()}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == i.get()); } } SUBCASE("deref") { { int i{42}; meta::uvalue u{&i}; CHECK(u.has_deref_op()); const meta::uvalue v{*u}; CHECK(v.get_as() == i); } { const char i{42}; meta::uvalue u{&i}; CHECK(u.has_deref_op()); const meta::uvalue v{*u}; CHECK(v.get_as() == i); } { const int i{42}; const int* const pi = &i; meta::uvalue u{&pi}; CHECK(u.has_deref_op()); const meta::uvalue v{*u}; CHECK(v.get_as() == pi); } { int i{42}; void* p1 = &i; const void* p2 = &i; void* const& p3 = &i; const void* const& p4 = &i; CHECK_FALSE(meta::uvalue(p1).has_deref_op()); CHECK_FALSE(meta::uvalue(p2).has_deref_op()); CHECK_FALSE(meta::uvalue(p3).has_deref_op()); CHECK_FALSE(meta::uvalue(p4).has_deref_op()); CHECK_FALSE(*meta::uvalue(p1)); CHECK_FALSE(*meta::uvalue(p2)); CHECK_FALSE(*meta::uvalue(p3)); CHECK_FALSE(*meta::uvalue(p4)); } { int* p1 = nullptr; const int* p2 = nullptr; CHECK(meta::uvalue{p1}.has_deref_op()); CHECK(meta::uvalue{p2}.has_deref_op()); CHECK_FALSE(*meta::uvalue{p1}); CHECK_FALSE(*meta::uvalue{p2}); } { ivec2 v{1,2}; meta::uvalue vp{&v}; CHECK(ivec2::move_constructor_counter == 0); CHECK(ivec2::copy_constructor_counter == 0); meta::uvalue vv1{*vp}; CHECK(ivec2::move_constructor_counter <= 3); CHECK(ivec2::copy_constructor_counter == 1); meta::uvalue vv2{*std::move(vp)}; CHECK(ivec2::move_constructor_counter <= 6); CHECK(ivec2::copy_constructor_counter == 2); meta::uvalue vv3{*std::as_const(vp)}; CHECK(ivec2::move_constructor_counter <= 9); CHECK(ivec2::copy_constructor_counter == 3); } { meta::uvalue v{std::make_shared(42)}; CHECK((*v).get_as() == 42); } } } TEST_CASE("meta/meta_utilities/value/arrays") { namespace meta = meta_hpp; SUBCASE("int") { meta::uvalue v{42}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); CHECK_FALSE(v[0]); } SUBCASE("void*") { int i{42}; void* p{&i}; meta::uvalue v{p}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); CHECK_FALSE(v[0]); } SUBCASE("const void*") { int i{42}; const void* p{&i}; meta::uvalue v{p}; CHECK(v.get_type() == meta::resolve_type()); CHECK_FALSE(v.has_index_op()); CHECK_FALSE(v[0]); } SUBCASE("int[3]") { { int arr[3]{1,2,3}; meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); } { int* arr = nullptr; meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); CHECK_FALSE(v[0]); } } SUBCASE("const int[3]") { { const int arr[3]{1,2,3}; meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); } { const int* arr = nullptr; meta::uvalue v{arr}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); CHECK_FALSE(v[0]); } } SUBCASE("std::array") { meta::uvalue v{std::array{1,2,3}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); CHECK_FALSE(v[3]); } SUBCASE("std::deque") { const meta::uvalue v{std::deque{1,2,3}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); CHECK_FALSE(v[3]); } SUBCASE("std::string") { meta::uvalue v{std::string{"hi!"}}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 'h'); CHECK(v[1].get_as() == 'i'); CHECK(v[2].get_as() == '!'); CHECK_FALSE(v[3]); } SUBCASE("std::span") { std::vector arr{1,2,3}; meta::uvalue v{std::span{arr}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); CHECK_FALSE(v[3]); } SUBCASE("std::vector") { const meta::uvalue v{std::vector{1,2,3}}; CHECK(v.get_type() == meta::resolve_type>()); CHECK(v.has_index_op()); CHECK(v[0].get_as() == 1); CHECK(v[1].get_as() == 2); CHECK(v[2].get_as() == 3); CHECK_FALSE(v[3]); } } TEST_CASE("meta/meta_utilities/value/functions") { namespace meta = meta_hpp; SUBCASE("add") { { meta::uvalue v{&ivec2::add}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == &ivec2::add); CHECK((ivec2{1,2}.*(v.get_as()))(ivec2{3,4}) == ivec2(4,6)); } { const meta::uvalue v{&ivec2::add}; CHECK(v.get_type() == meta::resolve_type()); CHECK(v.get_as() == &ivec2::add); CHECK((ivec2{1,2}.*(v.get_as()))(ivec2{3,4}) == ivec2(4,6)); } } SUBCASE("iadd2") { { meta::uvalue v{iadd2}; CHECK(v.get_type() == meta::resolve_type()); CHECK((v.get_as() == &iadd2)); CHECK((v.get_as())(ivec2{1,2}, ivec2{3,4}) == ivec2{4,6}); } { meta::uvalue v{&iadd2}; CHECK(v.get_type() == meta::resolve_type()); CHECK((v.get_as() == &iadd2)); CHECK((v.get_as())(ivec2{1,2}, ivec2{3,4}) == ivec2{4,6}); } { const meta::uvalue v{iadd2}; CHECK(v.get_type() == meta::resolve_type()); CHECK((v.get_as() == &iadd2)); CHECK((v.get_as())(ivec2{1,2}, ivec2{3,4}) == ivec2{4,6}); } { const meta::uvalue v{&iadd2}; CHECK(v.get_type() == meta::resolve_type()); CHECK((v.get_as() == &iadd2)); CHECK((v.get_as())(ivec2{1,2}, ivec2{3,4}) == ivec2{4,6}); } } }