diff --git a/TODO.md b/TODO.md index 7bad6d4..af4fed2 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,6 @@ - add array value access - add return value policy - add meta exception class; -- add conversion of nullptr to any pointers; - void value? - all string to hash? diff --git a/headers/meta.hpp/meta_all.hpp b/headers/meta.hpp/meta_all.hpp index 5fca320..3befe92 100644 --- a/headers/meta.hpp/meta_all.hpp +++ b/headers/meta.hpp/meta_all.hpp @@ -45,6 +45,7 @@ #include "meta_types/function_type.hpp" #include "meta_types/member_type.hpp" #include "meta_types/method_type.hpp" +#include "meta_types/nullptr_type.hpp" #include "meta_types/number_type.hpp" #include "meta_types/pointer_type.hpp" #include "meta_types/reference_type.hpp" diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index 7c6f43c..45ab91a 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -123,6 +123,7 @@ namespace meta_hpp class function_type; class member_type; class method_type; + class nullptr_type; class number_type; class pointer_type; class reference_type; @@ -138,6 +139,7 @@ namespace meta_hpp struct function_type_data; struct member_type_data; struct method_type_data; + struct nullptr_type_data; struct number_type_data; struct pointer_type_data; struct reference_type_data; @@ -151,6 +153,7 @@ namespace meta_hpp using function_type_data_ptr = std::shared_ptr; using member_type_data_ptr = std::shared_ptr; using method_type_data_ptr = std::shared_ptr; + using nullptr_type_data_ptr = std::shared_ptr; using number_type_data_ptr = std::shared_ptr; using pointer_type_data_ptr = std::shared_ptr; using reference_type_data_ptr = std::shared_ptr; diff --git a/headers/meta.hpp/meta_kinds.hpp b/headers/meta.hpp/meta_kinds.hpp index c524c13..9ff3d00 100644 --- a/headers/meta.hpp/meta_kinds.hpp +++ b/headers/meta.hpp/meta_kinds.hpp @@ -18,6 +18,7 @@ namespace meta_hpp function_, member_, method_, + nullptr_, number_, pointer_, reference_, @@ -45,6 +46,9 @@ namespace meta_hpp::detail template < typename T > concept method_kind = std::is_member_function_pointer_v; + template < typename T > + concept nullptr_kind = std::is_null_pointer_v; + template < typename T > concept number_kind = std::is_arithmetic_v; @@ -65,6 +69,7 @@ namespace meta_hpp::detail if constexpr ( function_kind ) { return type_kind::function_; } if constexpr ( member_kind ) { return type_kind::member_; } if constexpr ( method_kind ) { return type_kind::method_; } + if constexpr ( nullptr_kind ) { return type_kind::nullptr_; } if constexpr ( number_kind ) { return type_kind::number_; } if constexpr ( pointer_kind ) { return type_kind::pointer_; } if constexpr ( reference_kind ) { return type_kind::reference_; } @@ -119,6 +124,12 @@ namespace meta_hpp::detail using kind_type_data = method_type_data; }; + template <> + struct type_kind_traits { + using kind_type = nullptr_type; + using kind_type_data = nullptr_type_data; + }; + template <> struct type_kind_traits { using kind_type = number_type; diff --git a/headers/meta.hpp/meta_traits.hpp b/headers/meta.hpp/meta_traits.hpp index 41db8dc..cde2cd0 100644 --- a/headers/meta.hpp/meta_traits.hpp +++ b/headers/meta.hpp/meta_traits.hpp @@ -40,9 +40,6 @@ namespace meta_hpp::detail template < reference_kind Reference > struct reference_traits; - - template < void_kind Void > - struct void_traits; } namespace meta_hpp diff --git a/headers/meta.hpp/meta_types.hpp b/headers/meta.hpp/meta_types.hpp index 22fcf3a..ebf8a7e 100644 --- a/headers/meta.hpp/meta_types.hpp +++ b/headers/meta.hpp/meta_types.hpp @@ -72,6 +72,7 @@ namespace meta_hpp std::is_same_v || std::is_same_v || std::is_same_v || + std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || @@ -135,6 +136,7 @@ namespace meta_hpp any_type(const function_type& other) noexcept; any_type(const member_type& other) noexcept; any_type(const method_type& other) noexcept; + any_type(const nullptr_type& other) noexcept; any_type(const number_type& other) noexcept; any_type(const pointer_type& other) noexcept; any_type(const reference_type& other) noexcept; @@ -147,6 +149,7 @@ namespace meta_hpp [[nodiscard]] bool is_function() const noexcept; [[nodiscard]] bool is_member() const noexcept; [[nodiscard]] bool is_method() const noexcept; + [[nodiscard]] bool is_nullptr() const noexcept; [[nodiscard]] bool is_number() const noexcept; [[nodiscard]] bool is_pointer() const noexcept; [[nodiscard]] bool is_reference() const noexcept; @@ -159,6 +162,7 @@ namespace meta_hpp [[nodiscard]] function_type as_function() const noexcept; [[nodiscard]] member_type as_member() const noexcept; [[nodiscard]] method_type as_method() const noexcept; + [[nodiscard]] nullptr_type as_nullptr() const noexcept; [[nodiscard]] number_type as_number() const noexcept; [[nodiscard]] pointer_type as_pointer() const noexcept; [[nodiscard]] reference_type as_reference() const noexcept; @@ -352,6 +356,20 @@ namespace meta_hpp friend auto detail::data_access(const method_type&); }; + class nullptr_type final { + public: + nullptr_type() = default; + nullptr_type(detail::nullptr_type_data_ptr data); + + [[nodiscard]] bool is_valid() const noexcept; + [[nodiscard]] explicit operator bool() const noexcept; + + [[nodiscard]] type_id get_id() const noexcept; + private: + detail::nullptr_type_data_ptr data_; + friend auto detail::data_access(const nullptr_type&); + }; + class number_type final { public: number_type() = default; @@ -530,6 +548,14 @@ namespace meta_hpp::detail [[nodiscard]] static method_type_data_ptr get_static(); }; + struct nullptr_type_data final : type_data_base { + template < nullptr_kind Nullptr > + explicit nullptr_type_data(type_list); + + template < nullptr_kind Nullptr > + [[nodiscard]] static nullptr_type_data_ptr get_static(); + }; + struct number_type_data final : type_data_base { const bitflags flags; const std::size_t size; diff --git a/headers/meta.hpp/meta_types/any_type.hpp b/headers/meta.hpp/meta_types/any_type.hpp index 81716a9..2a4884c 100644 --- a/headers/meta.hpp/meta_types/any_type.hpp +++ b/headers/meta.hpp/meta_types/any_type.hpp @@ -51,6 +51,9 @@ namespace meta_hpp inline any_type::any_type(const method_type& other) noexcept : data_{detail::data_access(other)} {} + inline any_type::any_type(const nullptr_type& other) noexcept + : data_{detail::data_access(other)} {} + inline any_type::any_type(const number_type& other) noexcept : data_{detail::data_access(other)} {} @@ -91,6 +94,10 @@ namespace meta_hpp return data_ && data_->kind == type_kind::method_; } + inline bool any_type::is_nullptr() const noexcept { + return data_ && data_->kind == type_kind::nullptr_; + } + inline bool any_type::is_number() const noexcept { return data_ && data_->kind == type_kind::number_; } @@ -149,6 +156,12 @@ namespace meta_hpp : method_type{}; } + inline nullptr_type any_type::as_nullptr() const noexcept { + return is_nullptr() + ? nullptr_type{std::static_pointer_cast(data_)} + : nullptr_type{}; + } + inline number_type any_type::as_number() const noexcept { return is_number() ? number_type{std::static_pointer_cast(data_)} diff --git a/headers/meta.hpp/meta_types/nullptr_type.hpp b/headers/meta.hpp/meta_types/nullptr_type.hpp new file mode 100644 index 0000000..c566a9b --- /dev/null +++ b/headers/meta.hpp/meta_types/nullptr_type.hpp @@ -0,0 +1,45 @@ +/******************************************************************************* + * 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) + ******************************************************************************/ + +#pragma once + +#include "../meta_base.hpp" +#include "../meta_types.hpp" + +namespace meta_hpp::detail +{ + template < nullptr_kind Nullptr > + struct nullptr_tag {}; + + template < nullptr_kind Nullptr > + // NOLINTNEXTLINE(readability-named-parameter) + nullptr_type_data::nullptr_type_data(type_list) + : type_data_base{type_id{type_list>{}}, type_kind::nullptr_} {} + + template < nullptr_kind Nullptr > + nullptr_type_data_ptr nullptr_type_data::get_static() { + static nullptr_type_data_ptr data = std::make_shared(type_list{}); + return data; + } +} + +namespace meta_hpp +{ + inline nullptr_type::nullptr_type(detail::nullptr_type_data_ptr data) + : data_{std::move(data)} {} + + inline bool nullptr_type::is_valid() const noexcept { + return !!data_; + } + + inline nullptr_type::operator bool() const noexcept { + return is_valid(); + } + + inline type_id nullptr_type::get_id() const noexcept { + return data_->id; + } +} diff --git a/headers/meta.hpp/meta_utilities/arg.hpp b/headers/meta.hpp/meta_utilities/arg.hpp index f081759..c71de18 100644 --- a/headers/meta.hpp/meta_utilities/arg.hpp +++ b/headers/meta.hpp/meta_utilities/arg.hpp @@ -97,6 +97,10 @@ namespace meta_hpp::detail }; if constexpr ( std::is_pointer_v ) { + if ( to_type.is_pointer() && from_type.is_nullptr() ) { + return true; + } + 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); @@ -206,6 +210,10 @@ 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_nullptr() ) { + return static_cast(nullptr); + } + 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(); diff --git a/untests/meta_states/function_tests.cpp b/untests/meta_states/function_tests.cpp index 449e1d8..a5b24bc 100644 --- a/untests/meta_states/function_tests.cpp +++ b/untests/meta_states/function_tests.cpp @@ -20,6 +20,7 @@ namespace return v.x * v.x + v.y * v.y; } + static bool arg_nullptr(const void* ptr) { return ptr == nullptr; } 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; } @@ -37,6 +38,7 @@ TEST_CASE("meta/meta_states/function") { meta::class_() .function_("iadd", &ivec2::iadd) .function_("ilength2", &ivec2::ilength2) + .function_("arg_nullptr", &ivec2::arg_nullptr) .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) @@ -114,6 +116,19 @@ TEST_CASE("meta/meta_states/function") { CHECK(func.invoke(ivec2{2,3}).value() == 13); } + SUBCASE("arg_null") { + const meta::function func = ivec2_type.get_function("arg_nullptr"); + REQUIRE(func); + + CHECK(func.is_invocable_with()); + CHECK(func.is_invocable_with()); + CHECK(func.is_invocable_with()); + + int i{42}; + CHECK(func.invoke(&i) == false); + CHECK(func.invoke(nullptr) == true); + } + SUBCASE("arg_arr") { ivec2 bounded_arr[2]{{1,2},{3,4}}; ivec2* unbounded_arr = bounded_arr; diff --git a/untests/meta_types/any_type_tests.cpp b/untests/meta_types/any_type_tests.cpp index c9ab8f7..bf04fa7 100644 --- a/untests/meta_types/any_type_tests.cpp +++ b/untests/meta_types/any_type_tests.cpp @@ -162,10 +162,28 @@ TEST_CASE("meta/meta_types/any_type") { CHECK(type.is_method()); CHECK(type.get_kind() == meta::type_kind::method_); + CHECK_FALSE(type.is_nullptr()); + CHECK_FALSE(type.as_nullptr()); + + const meta::method_type& specific_type = type.as_method(); + REQUIRE(specific_type); + CHECK(specific_type.get_id() == type.get_id()); + } + + SUBCASE("nullptr") { + const meta::any_type& type = meta::resolve_type(nullptr); + + REQUIRE(type); + REQUIRE(type == meta::resolve_type()); + REQUIRE(type.get_id() == meta::resolve_type().get_id()); + + CHECK(type.is_nullptr()); + CHECK(type.get_kind() == meta::type_kind::nullptr_); + CHECK_FALSE(type.is_number()); CHECK_FALSE(type.as_number()); - const meta::method_type& specific_type = type.as_method(); + const meta::nullptr_type& specific_type = type.as_nullptr(); REQUIRE(specific_type); CHECK(specific_type.get_id() == type.get_id()); } diff --git a/untests/meta_utilities/arg7_tests.cpp b/untests/meta_utilities/arg7_tests.cpp index 4ffe3da..2fed809 100644 --- a/untests/meta_utilities/arg7_tests.cpp +++ b/untests/meta_utilities/arg7_tests.cpp @@ -46,13 +46,16 @@ TEST_CASE("meta/meta_utilities/arg7") { meta::class_().base_().base_(); } -TEST_CASE("meta/meta_utilities/arg7/cast") { +TEST_CASE("meta/meta_utilities/arg7/cast/to_void") { namespace meta = meta_hpp; using meta::detail::arg; - SUBCASE("int*") { + SUBCASE("int* -> void*") { int i{42}; + static_assert(std::is_invocable_v); + static_assert(std::is_invocable_v); + CHECK(arg{&i}.can_cast_to()); CHECK(arg{&i}.can_cast_to()); @@ -60,9 +63,12 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{&i}.cast() == &i); } - SUBCASE("const int*") { + SUBCASE("const int* -> void*") { const int i{42}; + static_assert(!std::is_invocable_v); + static_assert(std::is_invocable_v); + CHECK_FALSE(arg{&i}.can_cast_to()); CHECK(arg{&i}.can_cast_to()); @@ -70,7 +76,7 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{&i}.cast() == &i); } - SUBCASE("D*") { + SUBCASE("D* -> void*") { D d; static_assert(std::is_invocable_v); @@ -83,7 +89,7 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{&d}.cast() == &d); } - SUBCASE("const D*") { + SUBCASE("const D* -> void*") { const D d; static_assert(!std::is_invocable_v); @@ -96,7 +102,7 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{&d}.cast() == &d); } - SUBCASE("D[2]") { + SUBCASE("D[2] -> void*") { D arr[2]; static_assert(std::is_invocable_v); @@ -109,7 +115,7 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{arr}.cast() == &arr); } - SUBCASE("const D[2]") { + SUBCASE("const D[2] -> void*") { const D arr[2]; static_assert(!std::is_invocable_v); @@ -122,3 +128,69 @@ TEST_CASE("meta/meta_utilities/arg7/cast") { CHECK(arg{arr}.cast() == &arr); } } + +TEST_CASE("meta/meta_utilities/arg7/cast/from_nullptr") { + namespace meta = meta_hpp; + using meta::detail::arg; + + SUBCASE("nullptr -> *") { + 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); + + 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); + + nullptr_t n1{nullptr}; + const nullptr_t n2{nullptr}; + + CHECK(arg{n1}.can_cast_to()); + CHECK(arg{std::move(n1)}.can_cast_to()); + CHECK(arg{n2}.can_cast_to()); + CHECK(arg{std::move(n2)}.can_cast_to()); + + CHECK(arg{n1}.can_cast_to()); + CHECK(arg{std::move(n1)}.can_cast_to()); + CHECK(arg{n2}.can_cast_to()); + CHECK(arg{std::move(n2)}.can_cast_to()); + + CHECK(arg{n1}.can_cast_to()); + CHECK(arg{std::move(n1)}.can_cast_to()); + CHECK(arg{n2}.can_cast_to()); + CHECK(arg{std::move(n2)}.can_cast_to()); + + CHECK(arg{n1}.can_cast_to()); + CHECK(arg{std::move(n1)}.can_cast_to()); + CHECK(arg{n2}.can_cast_to()); + CHECK(arg{std::move(n2)}.can_cast_to()); + + // + + CHECK(arg{n1}.cast() == nullptr); + CHECK(arg{std::move(n1)}.cast() == nullptr); + CHECK(arg{n2}.cast() == nullptr); + CHECK(arg{std::move(n2)}.cast() == nullptr); + + CHECK(arg{n1}.cast() == nullptr); + CHECK(arg{std::move(n1)}.cast() == nullptr); + CHECK(arg{n2}.cast() == nullptr); + CHECK(arg{std::move(n2)}.cast() == nullptr); + + CHECK(arg{n1}.cast() == nullptr); + CHECK(arg{std::move(n1)}.cast() == nullptr); + CHECK(arg{n2}.cast() == nullptr); + CHECK(arg{std::move(n2)}.cast() == nullptr); + + CHECK(arg{n1}.cast() == nullptr); + CHECK(arg{std::move(n1)}.cast() == nullptr); + CHECK(arg{n2}.cast() == nullptr); + CHECK(arg{std::move(n2)}.cast() == nullptr); + } +}