From c277dbfc1c4ac6a334e0721ecc4deba321a853f8 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Fri, 14 Jan 2022 07:14:28 +0700 Subject: [PATCH] array arg support --- headers/meta.hpp/meta_utilities/arg.hpp | 36 +++ manuals/meta_examples/arrays_example.cpp | 15 -- manuals/meta_examples/numbers_example.cpp | 15 -- untests/meta_states/function_tests.cpp | 127 ++++++++++- untests/meta_types/function_type_tests.cpp | 53 +++++ untests/meta_utilities/arg5_tests.cpp | 254 +++++++++++++++++++++ 6 files changed, 469 insertions(+), 31 deletions(-) delete mode 100644 manuals/meta_examples/arrays_example.cpp delete mode 100644 manuals/meta_examples/numbers_example.cpp create mode 100644 untests/meta_utilities/arg5_tests.cpp diff --git a/headers/meta.hpp/meta_utilities/arg.hpp b/headers/meta.hpp/meta_utilities/arg.hpp index b987134..9e30664 100644 --- a/headers/meta.hpp/meta_utilities/arg.hpp +++ b/headers/meta.hpp/meta_utilities/arg.hpp @@ -87,6 +87,21 @@ namespace meta_hpp::detail }; if constexpr ( std::is_pointer_v ) { + if ( to_type.is_pointer() && from_type.is_array() ) { + const pointer_type& to_type_ptr = to_type.as_pointer(); + const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); + + const array_type& from_type_array = from_type.as_array(); + const bool from_type_array_readonly = is_const(); + + const any_type& to_data_type = to_type_ptr.get_data_type(); + const any_type& from_data_type = from_type_array.get_data_type(); + + if ( is_a(to_data_type, from_data_type) && to_type_ptr_readonly >= from_type_array_readonly ) { + return true; + } + } + if ( to_type.is_pointer() && from_type.is_pointer() ) { const pointer_type& to_type_ptr = to_type.as_pointer(); const bool to_type_ptr_readonly = to_type_ptr.get_flags().has(pointer_flags::is_readonly); @@ -177,6 +192,27 @@ namespace meta_hpp::detail const any_type& to_type = resolve_type(); if constexpr ( std::is_pointer_v ) { + if ( to_type.is_pointer() && from_type.is_array() ) { + const pointer_type& to_type_ptr = to_type.as_pointer(); + const array_type& from_type_array = from_type.as_array(); + + const any_type& to_data_type = to_type_ptr.get_data_type(); + const any_type& from_data_type = from_type_array.get_data_type(); + + if ( to_data_type == from_data_type ) { + void* to_ptr = static_cast(data_); + return static_cast(to_ptr); + } + + if ( to_data_type.is_class() && from_data_type.is_class() ) { + const class_type& to_data_class = to_data_type.as_class(); + const class_type& from_data_class = from_data_type.as_class(); + + void* to_ptr = detail::pointer_upcast(data_, from_data_class, to_data_class); + return static_cast(to_ptr); + } + } + if ( to_type.is_pointer() && from_type.is_pointer() ) { const pointer_type& to_type_ptr = to_type.as_pointer(); const pointer_type& from_type_ptr = from_type.as_pointer(); diff --git a/manuals/meta_examples/arrays_example.cpp b/manuals/meta_examples/arrays_example.cpp deleted file mode 100644 index 7c28b8b..0000000 --- a/manuals/meta_examples/arrays_example.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/******************************************************************************* - * 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, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#include "../meta_manuals.hpp" - -namespace -{ -} - -TEST_CASE("meta/meta_examples/arrays") { - namespace meta = meta_hpp; -} diff --git a/manuals/meta_examples/numbers_example.cpp b/manuals/meta_examples/numbers_example.cpp deleted file mode 100644 index dc0c989..0000000 --- a/manuals/meta_examples/numbers_example.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/******************************************************************************* - * 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, by Matvey Cherevko (blackmatov@gmail.com) - ******************************************************************************/ - -#include "../meta_manuals.hpp" - -namespace -{ -} - -TEST_CASE("meta/meta_examples/numbers") { - namespace meta = meta_hpp; -} diff --git a/untests/meta_states/function_tests.cpp b/untests/meta_states/function_tests.cpp index aeb30ea..449e1d8 100644 --- a/untests/meta_states/function_tests.cpp +++ b/untests/meta_states/function_tests.cpp @@ -19,6 +19,11 @@ namespace static int ilength2(const ivec2& v) noexcept { return v.x * v.x + v.y * v.y; } + + static int arg_bounded_arr(ivec2 vs[2]) { return vs[0].x + vs[0].y + vs[1].x + vs[1].y; } + static int arg_unbounded_arr(ivec2 vs[]) { return vs[0].x + vs[0].y + vs[1].x + vs[1].y; } + static int arg_bounded_const_arr(const ivec2 vs[2]) { return vs[0].x + vs[0].y + vs[1].x + vs[1].y; } + static int arg_unbounded_const_arr(const ivec2 vs[]) { return vs[0].x + vs[0].y + vs[1].x + vs[1].y; } }; bool operator==(const ivec2& l, const ivec2& r) noexcept { @@ -31,7 +36,11 @@ TEST_CASE("meta/meta_states/function") { meta::class_() .function_("iadd", &ivec2::iadd) - .function_("ilength2", &ivec2::ilength2); + .function_("ilength2", &ivec2::ilength2) + .function_("arg_bounded_arr", &ivec2::arg_bounded_arr) + .function_("arg_unbounded_arr", &ivec2::arg_unbounded_arr) + .function_("arg_bounded_const_arr", &ivec2::arg_bounded_const_arr) + .function_("arg_unbounded_const_arr", &ivec2::arg_unbounded_const_arr); const meta::class_type ivec2_type = meta::resolve_type(); REQUIRE(ivec2_type); @@ -104,4 +113,120 @@ TEST_CASE("meta/meta_states/function") { CHECK(func.invoke(ivec2{2,3})); CHECK(func.invoke(ivec2{2,3}).value() == 13); } + + SUBCASE("arg_arr") { + ivec2 bounded_arr[2]{{1,2},{3,4}}; + ivec2* unbounded_arr = bounded_arr; + + const ivec2 bounded_const_arr[2]{{1,2},{3,4}}; + const ivec2* unbounded_const_arr = bounded_const_arr; + + { + const meta::function func1 = ivec2_type.get_function("arg_bounded_arr"); + REQUIRE(func1); + + CHECK(func1.is_invocable_with()); + CHECK(func1.is_invocable_with()); + CHECK_FALSE(func1.is_invocable_with()); + CHECK_FALSE(func1.is_invocable_with()); + + CHECK(func1.invoke(bounded_arr) == 10); + CHECK(func1.invoke(unbounded_arr) == 10); + CHECK_THROWS(func1.invoke(bounded_const_arr)); + CHECK_THROWS(func1.invoke(unbounded_const_arr)); + + CHECK(func1.invoke(meta::value{bounded_arr}) == 10); + CHECK(func1.invoke(meta::value{unbounded_arr}) == 10); + CHECK_THROWS(func1.invoke(meta::value{bounded_const_arr})); + CHECK_THROWS(func1.invoke(meta::value{unbounded_const_arr})); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + } + + { + const meta::function func2 = ivec2_type.get_function("arg_unbounded_arr"); + REQUIRE(func2); + + CHECK(func2.is_invocable_with()); + CHECK(func2.is_invocable_with()); + CHECK_FALSE(func2.is_invocable_with()); + CHECK_FALSE(func2.is_invocable_with()); + + CHECK(func2.invoke(bounded_arr) == 10); + CHECK(func2.invoke(unbounded_arr) == 10); + CHECK_THROWS(func2.invoke(bounded_const_arr)); + CHECK_THROWS(func2.invoke(unbounded_const_arr)); + + CHECK(func2.invoke(meta::value{bounded_arr}) == 10); + CHECK(func2.invoke(meta::value{unbounded_arr}) == 10); + CHECK_THROWS(func2.invoke(meta::value{bounded_const_arr})); + CHECK_THROWS(func2.invoke(meta::value{unbounded_const_arr})); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(!std::is_invocable_v); + } + } + + SUBCASE("arg_const_arr") { + ivec2 bounded_arr[2]{{1,2},{3,4}}; + ivec2* unbounded_arr = bounded_arr; + + const ivec2 bounded_const_arr[2]{{1,2},{3,4}}; + const ivec2* unbounded_const_arr = bounded_const_arr; + + { + const meta::function func1 = ivec2_type.get_function("arg_bounded_const_arr"); + REQUIRE(func1); + + CHECK(func1.is_invocable_with()); + CHECK(func1.is_invocable_with()); + CHECK(func1.is_invocable_with()); + CHECK(func1.is_invocable_with()); + + CHECK(func1.invoke(bounded_arr) == 10); + CHECK(func1.invoke(unbounded_arr) == 10); + CHECK(func1.invoke(bounded_const_arr) == 10); + CHECK(func1.invoke(unbounded_const_arr) == 10); + + CHECK(func1.invoke(meta::value{bounded_arr}) == 10); + CHECK(func1.invoke(meta::value{unbounded_arr}) == 10); + CHECK(func1.invoke(meta::value{bounded_const_arr}) == 10); + CHECK(func1.invoke(meta::value{unbounded_const_arr}) == 10); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + } + + { + const meta::function func2 = ivec2_type.get_function("arg_unbounded_const_arr"); + REQUIRE(func2); + + CHECK(func2.is_invocable_with()); + CHECK(func2.is_invocable_with()); + CHECK(func2.is_invocable_with()); + CHECK(func2.is_invocable_with()); + + CHECK(func2.invoke(bounded_arr) == 10); + CHECK(func2.invoke(unbounded_arr) == 10); + CHECK(func2.invoke(bounded_const_arr) == 10); + CHECK(func2.invoke(unbounded_const_arr) == 10); + + CHECK(func2.invoke(meta::value{bounded_arr}) == 10); + CHECK(func2.invoke(meta::value{unbounded_arr}) == 10); + CHECK(func2.invoke(meta::value{bounded_const_arr}) == 10); + CHECK(func2.invoke(meta::value{unbounded_const_arr}) == 10); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + } + } } diff --git a/untests/meta_types/function_type_tests.cpp b/untests/meta_types/function_type_tests.cpp index 71562fb..9d02c58 100644 --- a/untests/meta_types/function_type_tests.cpp +++ b/untests/meta_types/function_type_tests.cpp @@ -16,6 +16,11 @@ namespace void arg_copy(ivec2) {} void arg_ref_noexcept(ivec2&) noexcept {} void arg_cref_noexcept(const ivec2&) noexcept {} + + void arg_bounded_arr(ivec2[2]) {} + void arg_unbounded_arr(ivec2[]) {} + void arg_bounded_const_arr(const ivec2[2]) {} + void arg_unbounded_const_arr(const ivec2[]) {} } TEST_CASE("meta/meta_types/function_type") { @@ -65,4 +70,52 @@ TEST_CASE("meta/meta_types/function_type") { CHECK(type.get_argument_type(0) == meta::resolve_type()); CHECK_FALSE(type.get_argument_type(1)); } + + SUBCASE("arg_bounded_arr") { + const meta::function_type type = meta::resolve_type(&arg_bounded_arr); + REQUIRE(type); + + CHECK(type.get_id() == meta::resolve_type(&arg_bounded_arr).get_id()); + CHECK(type.get_flags() == meta::function_flags{}); + CHECK(type.get_argument_types() == std::vector{meta::resolve_type()}); + + CHECK(type.get_argument_type(0) == meta::resolve_type()); + CHECK_FALSE(type.get_argument_type(1)); + } + + SUBCASE("arg_unbounded_arr") { + const meta::function_type type = meta::resolve_type(&arg_unbounded_arr); + REQUIRE(type); + + CHECK(type.get_id() == meta::resolve_type(&arg_unbounded_arr).get_id()); + CHECK(type.get_flags() == meta::function_flags{}); + CHECK(type.get_argument_types() == std::vector{meta::resolve_type()}); + + CHECK(type.get_argument_type(0) == meta::resolve_type()); + CHECK_FALSE(type.get_argument_type(1)); + } + + SUBCASE("arg_bounded_const_arr") { + const meta::function_type type = meta::resolve_type(&arg_bounded_const_arr); + REQUIRE(type); + + CHECK(type.get_id() == meta::resolve_type(&arg_bounded_const_arr).get_id()); + CHECK(type.get_flags() == meta::function_flags{}); + CHECK(type.get_argument_types() == std::vector{meta::resolve_type()}); + + CHECK(type.get_argument_type(0) == meta::resolve_type()); + CHECK_FALSE(type.get_argument_type(1)); + } + + SUBCASE("arg_unbounded_const_arr") { + const meta::function_type type = meta::resolve_type(&arg_unbounded_const_arr); + REQUIRE(type); + + CHECK(type.get_id() == meta::resolve_type(&arg_unbounded_const_arr).get_id()); + CHECK(type.get_flags() == meta::function_flags{}); + CHECK(type.get_argument_types() == std::vector{meta::resolve_type()}); + + CHECK(type.get_argument_type(0) == meta::resolve_type()); + CHECK_FALSE(type.get_argument_type(1)); + } } diff --git a/untests/meta_utilities/arg5_tests.cpp b/untests/meta_utilities/arg5_tests.cpp new file mode 100644 index 0000000..3275e88 --- /dev/null +++ b/untests/meta_utilities/arg5_tests.cpp @@ -0,0 +1,254 @@ +/******************************************************************************* + * 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, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_untests.hpp" + +namespace +{ + struct A { + A() = default; + virtual ~A() = default; + + A(A&&) = delete; + A(const A&) = delete; + A& operator=(A&&) = delete; + A& operator=(const A&) = delete; + + int i = 1; + }; + + struct B : virtual A { + int i = 2; + }; + + struct C : virtual A { + int i = 3; + }; + + struct D : B, C { + int i = 4; + }; +} + +TEST_CASE("meta/meta_utilities/arg5") { + namespace meta = meta_hpp; + + // * <- B <- * + // A D + // * <- C <- * + + meta::class_(); + meta::class_().base_(); + meta::class_().base_(); + meta::class_().base_().base_(); +} + +TEST_CASE("meta/meta_utilities/arg5/cast") { + namespace meta = meta_hpp; + using meta::detail::arg; + + SUBCASE("int[2]") { + int arr[2]{1,2}; + CHECK(arg(arr).get_raw_type() == meta::resolve_type(arr)); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + } + + SUBCASE("const int[2]") { + const int arr[2]{1,2}; + CHECK(arg(arr).get_raw_type() == meta::resolve_type(arr)); + + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_FALSE(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK_FALSE(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + + CHECK_THROWS(std::ignore = arg(arr).cast()); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK_THROWS(std::ignore = arg(arr).cast()); + CHECK(arg(arr).cast() == static_cast(arr)); + } + + SUBCASE("D[2]") { + D arr[2]; + CHECK(arg(arr).get_raw_type() == meta::resolve_type(arr)); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK(arg(arr).cast() == static_cast(arr)); + } + + SUBCASE("const D[2]") { + const D arr[2]; + CHECK(arg(arr).get_raw_type() == meta::resolve_type(arr)); + + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_FALSE(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + CHECK_FALSE(arg(arr).can_cast_to()); + CHECK(arg(arr).can_cast_to()); + + CHECK_THROWS(std::ignore = arg(arr).cast()); + CHECK(arg(arr).cast() == static_cast(arr)); + CHECK_THROWS(std::ignore = arg(arr).cast()); + CHECK(arg(arr).cast() == static_cast(arr)); + } + + SUBCASE("&") { + using T = D[2]; + static T src{}; + + { + auto LV = []() -> T& { return src; }; + CHECK(arg{LV()}.get_raw_type() == meta::resolve_type()); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_NOTHROW([](A*){}(LV())); + CHECK_NOTHROW([](const A*){}(LV())); + CHECK_NOTHROW([](A* const){}(LV())); + CHECK_NOTHROW([](const A* const){}(LV())); + + CHECK(arg(LV()).cast() == static_cast(src)); + CHECK(arg(LV()).cast() == static_cast(src)); + CHECK(arg(LV()).cast() == static_cast(src)); + CHECK(arg(LV()).cast() == static_cast(src)); + } + + { + auto CLV = []() -> const T& { return src; }; + CHECK(arg{CLV()}.get_raw_type() == meta::resolve_type()); + + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_THROWS(std::ignore = arg(CLV()).cast()); + CHECK(arg(CLV()).cast() == static_cast(src)); + CHECK_THROWS(std::ignore = arg(CLV()).cast()); + CHECK(arg(CLV()).cast() == static_cast(src)); + } + + { + auto XV = []() -> T&& { return std::move(src); }; + CHECK(arg{XV()}.get_raw_type() == meta::resolve_type()); + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK(arg(XV()).cast() == static_cast(src)); + CHECK(arg(XV()).cast() == static_cast(src)); + CHECK(arg(XV()).cast() == static_cast(src)); + CHECK(arg(XV()).cast() == static_cast(src)); + } + + { + auto CXV = []() -> const T&& { return std::move(src); }; + CHECK(arg{CXV()}.get_raw_type() == meta::resolve_type()); + + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_THROWS(std::ignore = arg(CXV()).cast()); + CHECK(arg(CXV()).cast() == static_cast(src)); + CHECK_THROWS(std::ignore = arg(CXV()).cast()); + CHECK(arg(CXV()).cast() == static_cast(src)); + } + } + + SUBCASE("*") { + { + static D arr[2]{}; + + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK(arg{arr}.can_cast_to()); + CHECK(arg{arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + + CHECK(&arg{arr}.cast() == &arr); + CHECK(&arg{arr}.cast() == &arr); + CHECK(arg{&arr}.cast() == &arr); + CHECK(arg{&arr}.cast() == &arr); + CHECK(arg{&arr}.cast() == &arr); + CHECK(arg{&arr}.cast() == &arr); + } + + { + static const D arr[2]{}; + + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + + CHECK_FALSE(arg{arr}.can_cast_to()); + CHECK(arg{arr}.can_cast_to()); + CHECK_FALSE(arg{&arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + CHECK_FALSE(arg{&arr}.can_cast_to()); + CHECK(arg{&arr}.can_cast_to()); + + CHECK_THROWS(std::ignore = &arg{arr}.cast()); + CHECK(&arg{arr}.cast() == &arr); + CHECK_THROWS(std::ignore = arg{&arr}.cast()); + CHECK(arg{&arr}.cast() == &arr); + CHECK_THROWS(std::ignore = arg{&arr}.cast()); + CHECK(arg{&arr}.cast() == &arr); + } + } +}