array arg support

This commit is contained in:
BlackMATov
2022-01-14 07:14:28 +07:00
parent 7cfce057d7
commit c277dbfc1c
6 changed files with 469 additions and 31 deletions

View File

@@ -87,6 +87,21 @@ namespace meta_hpp::detail
};
if constexpr ( std::is_pointer_v<To> ) {
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<to_raw_type>();
if constexpr ( std::is_pointer_v<To> ) {
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<void*>(data_);
return static_cast<to_raw_type_cv>(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_raw_type_cv>(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();

View File

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

View File

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

View File

@@ -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_<ivec2>()
.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<ivec2>();
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<ivec2*>());
CHECK(func1.is_invocable_with<ivec2* const>());
CHECK_FALSE(func1.is_invocable_with<const ivec2*>());
CHECK_FALSE(func1.is_invocable_with<const ivec2* const>());
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<decltype(&ivec2::arg_bounded_arr), decltype(bounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_bounded_arr), decltype(unbounded_arr)>);
static_assert(!std::is_invocable_v<decltype(&ivec2::arg_bounded_arr), decltype(bounded_const_arr)>);
static_assert(!std::is_invocable_v<decltype(&ivec2::arg_bounded_arr), decltype(unbounded_const_arr)>);
}
{
const meta::function func2 = ivec2_type.get_function("arg_unbounded_arr");
REQUIRE(func2);
CHECK(func2.is_invocable_with<ivec2*>());
CHECK(func2.is_invocable_with<ivec2* const>());
CHECK_FALSE(func2.is_invocable_with<const ivec2*>());
CHECK_FALSE(func2.is_invocable_with<const ivec2* const>());
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<decltype(&ivec2::arg_unbounded_arr), decltype(bounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_unbounded_arr), decltype(unbounded_arr)>);
static_assert(!std::is_invocable_v<decltype(&ivec2::arg_unbounded_arr), decltype(bounded_const_arr)>);
static_assert(!std::is_invocable_v<decltype(&ivec2::arg_unbounded_arr), decltype(unbounded_const_arr)>);
}
}
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<ivec2*>());
CHECK(func1.is_invocable_with<ivec2* const>());
CHECK(func1.is_invocable_with<const ivec2*>());
CHECK(func1.is_invocable_with<const ivec2* const>());
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<decltype(&ivec2::arg_bounded_const_arr), decltype(bounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_bounded_const_arr), decltype(unbounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_bounded_const_arr), decltype(bounded_const_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_bounded_const_arr), decltype(unbounded_const_arr)>);
}
{
const meta::function func2 = ivec2_type.get_function("arg_unbounded_const_arr");
REQUIRE(func2);
CHECK(func2.is_invocable_with<ivec2*>());
CHECK(func2.is_invocable_with<ivec2* const>());
CHECK(func2.is_invocable_with<const ivec2*>());
CHECK(func2.is_invocable_with<const ivec2* const>());
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<decltype(&ivec2::arg_unbounded_const_arr), decltype(bounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_unbounded_const_arr), decltype(unbounded_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_unbounded_const_arr), decltype(bounded_const_arr)>);
static_assert(std::is_invocable_v<decltype(&ivec2::arg_unbounded_const_arr), decltype(unbounded_const_arr)>);
}
}
}

View File

@@ -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<const ivec2&>());
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::any_type>{meta::resolve_type<ivec2*>()});
CHECK(type.get_argument_type(0) == meta::resolve_type<ivec2*>());
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::any_type>{meta::resolve_type<ivec2*>()});
CHECK(type.get_argument_type(0) == meta::resolve_type<ivec2*>());
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::any_type>{meta::resolve_type<const ivec2*>()});
CHECK(type.get_argument_type(0) == meta::resolve_type<const ivec2*>());
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::any_type>{meta::resolve_type<const ivec2*>()});
CHECK(type.get_argument_type(0) == meta::resolve_type<const ivec2*>());
CHECK_FALSE(type.get_argument_type(1));
}
}

View File

@@ -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_<A>();
meta::class_<B>().base_<A>();
meta::class_<C>().base_<A>();
meta::class_<D>().base_<B>().base_<C>();
}
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<void(int*), int (&) [2]>);
static_assert(std::is_invocable_v<void(const int*), int (&) [2]>);
static_assert(std::is_invocable_v<void(int* const), int (&) [2]>);
static_assert(std::is_invocable_v<void(const int* const), int (&) [2]>);
CHECK(arg(arr).can_cast_to<int*>());
CHECK(arg(arr).can_cast_to<const int*>());
CHECK(arg(arr).can_cast_to<int* const>());
CHECK(arg(arr).can_cast_to<const int* const>());
CHECK(arg(arr).cast<int*>() == static_cast<int*>(arr));
CHECK(arg(arr).cast<const int*>() == static_cast<const int*>(arr));
CHECK(arg(arr).cast<int* const>() == static_cast<int*>(arr));
CHECK(arg(arr).cast<const int* const>() == static_cast<const int*>(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<void(int*), const int (&) [2]>);
static_assert(std::is_invocable_v<void(const int*), const int (&) [2]>);
static_assert(!std::is_invocable_v<void(int* const), const int (&) [2]>);
static_assert(std::is_invocable_v<void(const int* const), const int (&) [2]>);
CHECK_FALSE(arg(arr).can_cast_to<int*>());
CHECK(arg(arr).can_cast_to<const int*>());
CHECK_FALSE(arg(arr).can_cast_to<int* const>());
CHECK(arg(arr).can_cast_to<const int* const>());
CHECK_THROWS(std::ignore = arg(arr).cast<int*>());
CHECK(arg(arr).cast<const int*>() == static_cast<const int*>(arr));
CHECK_THROWS(std::ignore = arg(arr).cast<int* const>());
CHECK(arg(arr).cast<const int* const>() == static_cast<const int*>(arr));
}
SUBCASE("D[2]") {
D arr[2];
CHECK(arg(arr).get_raw_type() == meta::resolve_type(arr));
static_assert(std::is_invocable_v<void(A*), D (&) [2]>);
static_assert(std::is_invocable_v<void(const A*), D (&) [2]>);
static_assert(std::is_invocable_v<void(A* const), D (&) [2]>);
static_assert(std::is_invocable_v<void(const A* const), D (&) [2]>);
CHECK(arg(arr).can_cast_to<A*>());
CHECK(arg(arr).can_cast_to<const A*>());
CHECK(arg(arr).can_cast_to<A* const>());
CHECK(arg(arr).can_cast_to<const A* const>());
CHECK(arg(arr).cast<A*>() == static_cast<A*>(arr));
CHECK(arg(arr).cast<const A*>() == static_cast<const A*>(arr));
CHECK(arg(arr).cast<A* const>() == static_cast<A*>(arr));
CHECK(arg(arr).cast<const A* const>() == static_cast<const A*>(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<void(A*), const D (&) [2]>);
static_assert(std::is_invocable_v<void(const A*), const D (&) [2]>);
static_assert(!std::is_invocable_v<void(A* const), const D (&) [2]>);
static_assert(std::is_invocable_v<void(const A* const), const D (&) [2]>);
CHECK_FALSE(arg(arr).can_cast_to<A*>());
CHECK(arg(arr).can_cast_to<const A*>());
CHECK_FALSE(arg(arr).can_cast_to<A* const>());
CHECK(arg(arr).can_cast_to<const A* const>());
CHECK_THROWS(std::ignore = arg(arr).cast<A*>());
CHECK(arg(arr).cast<const A*>() == static_cast<const A*>(arr));
CHECK_THROWS(std::ignore = arg(arr).cast<A* const>());
CHECK(arg(arr).cast<const A* const>() == static_cast<const A*>(arr));
}
SUBCASE("&") {
using T = D[2];
static T src{};
{
auto LV = []() -> T& { return src; };
CHECK(arg{LV()}.get_raw_type() == meta::resolve_type<D[2]>());
static_assert(std::is_invocable_v<void(A*), decltype(LV())>);
static_assert(std::is_invocable_v<void(const A*), decltype(LV())>);
static_assert(std::is_invocable_v<void(A* const), decltype(LV())>);
static_assert(std::is_invocable_v<void(const A* const), decltype(LV())>);
CHECK_NOTHROW([](A*){}(LV()));
CHECK_NOTHROW([](const A*){}(LV()));
CHECK_NOTHROW([](A* const){}(LV()));
CHECK_NOTHROW([](const A* const){}(LV()));
CHECK(arg(LV()).cast<A*>() == static_cast<A*>(src));
CHECK(arg(LV()).cast<const A*>() == static_cast<const A*>(src));
CHECK(arg(LV()).cast<A* const>() == static_cast<A*>(src));
CHECK(arg(LV()).cast<const A* const>() == static_cast<const A*>(src));
}
{
auto CLV = []() -> const T& { return src; };
CHECK(arg{CLV()}.get_raw_type() == meta::resolve_type<D[2]>());
static_assert(!std::is_invocable_v<void(A*), decltype(CLV())>);
static_assert(std::is_invocable_v<void(const A*), decltype(CLV())>);
static_assert(!std::is_invocable_v<void(A* const), decltype(CLV())>);
static_assert(std::is_invocable_v<void(const A* const), decltype(CLV())>);
CHECK_THROWS(std::ignore = arg(CLV()).cast<A*>());
CHECK(arg(CLV()).cast<const A*>() == static_cast<const A*>(src));
CHECK_THROWS(std::ignore = arg(CLV()).cast<A* const>());
CHECK(arg(CLV()).cast<const A* const>() == static_cast<const A*>(src));
}
{
auto XV = []() -> T&& { return std::move(src); };
CHECK(arg{XV()}.get_raw_type() == meta::resolve_type<D[2]>());
static_assert(std::is_invocable_v<void(A*), decltype(XV())>);
static_assert(std::is_invocable_v<void(const A*), decltype(XV())>);
static_assert(std::is_invocable_v<void(A* const), decltype(XV())>);
static_assert(std::is_invocable_v<void(const A* const), decltype(XV())>);
CHECK(arg(XV()).cast<A*>() == static_cast<A*>(src));
CHECK(arg(XV()).cast<const A*>() == static_cast<const A*>(src));
CHECK(arg(XV()).cast<A* const>() == static_cast<A*>(src));
CHECK(arg(XV()).cast<const A* const>() == static_cast<const A*>(src));
}
{
auto CXV = []() -> const T&& { return std::move(src); };
CHECK(arg{CXV()}.get_raw_type() == meta::resolve_type<D[2]>());
static_assert(!std::is_invocable_v<void(A*), decltype(CXV())>);
static_assert(std::is_invocable_v<void(const A*), decltype(CXV())>);
static_assert(!std::is_invocable_v<void(A* const), decltype(CXV())>);
static_assert(std::is_invocable_v<void(const A* const), decltype(CXV())>);
CHECK_THROWS(std::ignore = arg(CXV()).cast<A*>());
CHECK(arg(CXV()).cast<const A*>() == static_cast<const A*>(src));
CHECK_THROWS(std::ignore = arg(CXV()).cast<A* const>());
CHECK(arg(CXV()).cast<const A* const>() == static_cast<const A*>(src));
}
}
SUBCASE("*") {
{
static D arr[2]{};
static_assert(std::is_invocable_v<void(D (&) [2]), D (&) [2]>);
static_assert(std::is_invocable_v<void(const D (&) [2]), D (&) [2]>);
static_assert(std::is_invocable_v<void(D (*) [2]), D (*) [2]>);
static_assert(std::is_invocable_v<void(const D (*) [2]), D (*) [2]>);
static_assert(std::is_invocable_v<void(D (* const) [2]), D (*) [2]>);
static_assert(std::is_invocable_v<void(const D (* const) [2]), D (*) [2]>);
CHECK(arg{arr}.can_cast_to<D (&) [2]>());
CHECK(arg{arr}.can_cast_to<const D (&) [2]>());
CHECK(arg{&arr}.can_cast_to<D (*) [2]>());
CHECK(arg{&arr}.can_cast_to<const D (*) [2]>());
CHECK(arg{&arr}.can_cast_to<D (* const) [2]>());
CHECK(arg{&arr}.can_cast_to<const D (* const) [2]>());
CHECK(&arg{arr}.cast<D (&) [2]>() == &arr);
CHECK(&arg{arr}.cast<const D (&) [2]>() == &arr);
CHECK(arg{&arr}.cast<D (*) [2]>() == &arr);
CHECK(arg{&arr}.cast<const D (*) [2]>() == &arr);
CHECK(arg{&arr}.cast<D (* const) [2]>() == &arr);
CHECK(arg{&arr}.cast<const D (* const) [2]>() == &arr);
}
{
static const D arr[2]{};
static_assert(!std::is_invocable_v<void(D (&) [2]), const D (&) [2]>);
static_assert(std::is_invocable_v<void(const D (&) [2]), const D (&) [2]>);
static_assert(!std::is_invocable_v<void(D (*) [2]), const D (*) [2]>);
static_assert(std::is_invocable_v<void(const D (*) [2]), const D (*) [2]>);
static_assert(!std::is_invocable_v<void(D (* const) [2]), const D (*) [2]>);
static_assert(std::is_invocable_v<void(const D (* const) [2]), const D (*) [2]>);
CHECK_FALSE(arg{arr}.can_cast_to<D (&) [2]>());
CHECK(arg{arr}.can_cast_to<const D (&) [2]>());
CHECK_FALSE(arg{&arr}.can_cast_to<D (*) [2]>());
CHECK(arg{&arr}.can_cast_to<const D (*) [2]>());
CHECK_FALSE(arg{&arr}.can_cast_to<D (* const) [2]>());
CHECK(arg{&arr}.can_cast_to<const D (* const) [2]>());
CHECK_THROWS(std::ignore = &arg{arr}.cast<D (&) [2]>());
CHECK(&arg{arr}.cast<const D (&) [2]>() == &arr);
CHECK_THROWS(std::ignore = arg{&arr}.cast<D (*) [2]>());
CHECK(arg{&arr}.cast<const D (*) [2]>() == &arr);
CHECK_THROWS(std::ignore = arg{&arr}.cast<D (* const) [2]>());
CHECK(arg{&arr}.cast<const D (* const) [2]>() == &arr);
}
}
}