diff --git a/TODO b/TODO index e9b2c8382..3d130a86c 100644 --- a/TODO +++ b/TODO @@ -34,8 +34,10 @@ * use [[nodiscard]] consistently for safety purposes * make meta work across boundaries - - type(id, props...) -> type(id).properties(props...) with internal meta_prop_node ** as a sink + - ::prop: use meta_any as arguments rather than sfinae all around + - extend and make factory::prop more flexible - entt::reflect().type("foo"_hs, entt::as_property); or similar - - name-less reflect with properties - forbid reflecting two times the same type + - name-less reflect/type (for named types) + - a better context - tests, doc diff --git a/src/entt/core/type_traits.hpp b/src/entt/core/type_traits.hpp index d9d1457db..c6e36769d 100644 --- a/src/entt/core/type_traits.hpp +++ b/src/entt/core/type_traits.hpp @@ -120,6 +120,20 @@ template using type_list_unique_t = typename type_list_unique::type; +/** + * @brief A class to use to push around constexpr properties, nothing more. + * @tparam Key Property key. + * @tparam Value Property value. + */ +template +struct property { + /*! @brief Property key. */ + static constexpr auto key = Key; + /*! @brief Property value. */ + static constexpr auto value = Value; +}; + + /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index 0c82f8df1..7426d71e1 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -9,6 +9,7 @@ #include #include #include "../config/config.h" +#include "../core/type_traits.hpp" #include "policy.hpp" #include "meta.hpp" @@ -235,6 +236,10 @@ meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_ */ +template +class extended_meta_factory; + + /** * @brief A meta factory to be used for reflection purposes. * @@ -257,58 +262,22 @@ class meta_factory { return node && (node->identifier == identifier || duplicate(identifier, node->next)); } - bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { - return node && (node->key() == key || duplicate(key, node->next)); - } - - template - internal::meta_prop_node * properties() { - return nullptr; - } - - template - internal::meta_prop_node * properties(Property &&property, Other &&... other) { - static std::remove_cv_t> prop{}; - - static internal::meta_prop_node node{ - nullptr, - []() -> meta_any { - return std::as_const(std::get<0>(prop)); - }, - []() -> meta_any { - return std::as_const(std::get<1>(prop)); - } - }; - - prop = std::forward(property); - node.next = properties(std::forward(other)...); - ENTT_ASSERT(!duplicate(meta_any{std::get<0>(prop)}, node.next)); - return &node; - } - public: - /*! @brief Default constructor. */ - meta_factory() ENTT_NOEXCEPT = default; - /** - * @brief Extends a meta type by assigning it an identifier and properties. - * @tparam Property Types of properties to assign to the meta type. + * @brief Extends a meta type by assigning it an identifier. * @param identifier Unique identifier. - * @param property Properties to assign to the meta type. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { + auto type(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { auto * const node = internal::meta_info::resolve(); node->identifier = identifier; - node->prop = properties(std::forward(property)...); ENTT_ASSERT(!duplicate(identifier, *internal::meta_info<>::ctx)); ENTT_ASSERT(!duplicate(node, *internal::meta_info<>::ctx)); node->next = *internal::meta_info<>::ctx; *internal::meta_info<>::ctx = node; - return *this; + return extended_meta_factory{&node->prop}; } /** @@ -320,7 +289,7 @@ public: * @return A meta factory for the parent type. */ template - meta_factory base() ENTT_NOEXCEPT { + auto base() ENTT_NOEXCEPT { static_assert(std::is_base_of_v); auto * const type = internal::meta_info::resolve(); @@ -337,7 +306,7 @@ public: node.next = type->base; type->base = &node; - return *this; + return meta_factory{}; } /** @@ -350,7 +319,7 @@ public: * @return A meta factory for the parent type. */ template - meta_factory conv() ENTT_NOEXCEPT { + auto conv() ENTT_NOEXCEPT { static_assert(std::is_convertible_v); auto * const type = internal::meta_info::resolve(); @@ -367,7 +336,7 @@ public: node.next = type->conv; type->conv = &node; - return *this; + return meta_factory{}; } /** @@ -383,7 +352,7 @@ public: * @return A meta factory for the parent type. */ template - meta_factory conv() ENTT_NOEXCEPT { + auto conv() ENTT_NOEXCEPT { using conv_type = std::invoke_result_t; auto * const type = internal::meta_info::resolve(); @@ -400,7 +369,7 @@ public: node.next = type->conv; type->conv = &node; - return *this; + return meta_factory{}; } /** @@ -414,12 +383,10 @@ public: * * @tparam Func The actual function to use as a constructor. * @tparam Policy Optional policy (no policy set by default). - * @tparam Property Types of properties to assign to the meta data. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory ctor(Property &&... property) ENTT_NOEXCEPT { + template + auto ctor() ENTT_NOEXCEPT { using helper_type = internal::meta_function_helper_t; static_assert(std::is_same_v); auto * const type = internal::meta_info::resolve(); @@ -435,12 +402,11 @@ public: } }; - node.prop = properties(std::forward(property)...); ENTT_ASSERT(!duplicate(&node, type->ctor)); node.next = type->ctor; type->ctor = &node; - return *this; + return extended_meta_factory>{&node.prop}; } /** @@ -451,12 +417,10 @@ public: * type that can be invoked with parameters whose types are those given. * * @tparam Args Types of arguments to use to construct an instance. - * @tparam Property Types of properties to assign to the meta data. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory ctor(Property &&... property) ENTT_NOEXCEPT { + template + auto ctor() ENTT_NOEXCEPT { using helper_type = internal::meta_function_helper_t; auto * const type = internal::meta_info::resolve(); @@ -471,12 +435,11 @@ public: } }; - node.prop = properties(std::forward(property)...); ENTT_ASSERT(!duplicate(&node, type->ctor)); node.next = type->ctor; type->ctor = &node; - return *this; + return extended_meta_factory{&node.prop}; } /** @@ -496,7 +459,7 @@ public: * @return A meta factory for the parent type. */ template - meta_factory dtor() ENTT_NOEXCEPT { + auto dtor() ENTT_NOEXCEPT { static_assert(std::is_invocable_v); auto * const type = internal::meta_info::resolve(); @@ -516,7 +479,7 @@ public: ENTT_ASSERT(!type->dtor); type->dtor = &node; - return *this; + return meta_factory{}; } /** @@ -529,13 +492,11 @@ public: * * @tparam Data The actual variable to attach to the meta type. * @tparam Policy Optional policy (no policy set by default). - * @tparam Property Types of properties to assign to the meta data. * @param identifier Unique identifier. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { + template + auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { auto * const type = internal::meta_info::resolve(); internal::meta_data_node *curr = nullptr; @@ -554,7 +515,6 @@ public: [](meta_handle, meta_any) -> meta_any { return Data; } }; - node.prop = properties>(std::forward(property)...); curr = &node; } else if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t().*Data)>; @@ -571,7 +531,6 @@ public: &internal::getter }; - node.prop = properties>(std::forward(property)...); curr = &node; } else { static_assert(std::is_pointer_v>); @@ -589,7 +548,6 @@ public: &internal::getter }; - node.prop = properties>(std::forward(property)...); curr = &node; } @@ -599,7 +557,7 @@ public: curr->next = type->data; type->data = curr; - return *this; + return extended_meta_factory>{&curr->prop}; } /** @@ -619,14 +577,11 @@ public: * @tparam Setter The actual function to use as a setter. * @tparam Getter The actual function to use as a getter. * @tparam Policy Optional policy (no policy set by default). - * @tparam Property Types of properties to assign to the meta data. * @param identifier Unique identifier. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - using owner_type = std::tuple, std::integral_constant>; + template + auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { using underlying_type = std::invoke_result_t; static_assert(std::is_invocable_v); auto * const type = internal::meta_info::resolve(); @@ -644,13 +599,12 @@ public: }; node.identifier = identifier; - node.prop = properties(std::forward(property)...); ENTT_ASSERT(!duplicate(identifier, type->data)); ENTT_ASSERT(!duplicate(&node, type->data)); node.next = type->data; type->data = &node; - return *this; + return extended_meta_factory, std::integral_constant>{&node.prop}; } /** @@ -663,14 +617,11 @@ public: * * @tparam Candidate The actual function to attach to the meta type. * @tparam Policy Optional policy (no policy set by default). - * @tparam Property Types of properties to assign to the meta function. * @param identifier Unique identifier. - * @param property Properties to assign to the meta function. - * @return A meta factory for the parent type. + * @return An extended meta factory for the parent type. */ - template - meta_factory func(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - using owner_type = std::integral_constant; + template + auto func(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { using helper_type = internal::meta_function_helper_t; auto * const type = internal::meta_info::resolve(); @@ -690,13 +641,12 @@ public: }; node.identifier = identifier; - node.prop = properties(std::forward(property)...); ENTT_ASSERT(!duplicate(identifier, type->func)); ENTT_ASSERT(!duplicate(&node, type->func)); node.next = type->func; type->func = &node; - return *this; + return extended_meta_factory>{&node.prop}; } /** @@ -713,6 +663,57 @@ public: }; +/** + * @brief Extended meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template +class extended_meta_factory: public meta_factory { + /*! @brief A meta factory is allowed to _extend_ itself. */ + friend class meta_factory; + + bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { + return node && (node->key() == key || duplicate(key, node->next)); + } + + template + void prop(property) { + static internal::meta_prop_node node{ + *props, + []() -> meta_any { + return Key; + }, + []() -> meta_any { + return Value; + } + }; + + ENTT_ASSERT(!duplicate(meta_any{Key}, *props)); + *props = &node; + } + + extended_meta_factory(entt::internal::meta_prop_node **target) + : props{target} + {} + +public: + /** + * @brief Assigns properties to the last meta object created. + * @tparam Property Properties to assign to the meta object. + * @return A meta factory for the parent type. + */ + template + auto prop() { + ENTT_ASSERT(props); + (prop(Property{}), ...); + return *this; + } + +private: + entt::internal::meta_prop_node **props{nullptr}; +}; + + /** * @brief Utility function to use for reflection. * diff --git a/test/entt/meta/meta.cpp b/test/entt/meta/meta.cpp index 5f5bb5485..dd531d805 100644 --- a/test/entt/meta/meta.cpp +++ b/test/entt/meta/meta.cpp @@ -146,12 +146,16 @@ struct Meta: ::testing::Test { entt::meta().conv(); entt::meta() - .type("char"_hs, std::make_pair(properties::prop_int, 42)) + .type("char"_hs) + .prop>() .data<&set, &get>("value"_hs); entt::meta() .data("prop_bool"_hs) + .prop>() .data("prop_int"_hs) + .prop>() + .prop>() .data<&set, &get>("value"_hs); entt::meta().data<0u>("min"_hs).data<100u>("max"_hs); @@ -160,10 +164,13 @@ struct Meta: ::testing::Test { .type("base"_hs); entt::meta() - .type("derived"_hs, std::make_pair(properties::prop_int, 99)) + .type("derived"_hs) + .prop>() .base() - .ctor(std::make_pair(properties::prop_bool, false)) - .ctor<&derived_factory>(std::make_pair(properties::prop_int, 42)) + .ctor() + .prop>() + .ctor<&derived_factory>() + .prop>() .conv<&derived_type::f>() .conv<&derived_type::g>(); @@ -178,10 +185,14 @@ struct Meta: ::testing::Test { entt::meta() .type("data"_hs) - .data<&data_type::i, entt::as_alias_t>("i"_hs, std::make_pair(properties::prop_int, 0)) - .data<&data_type::j>("j"_hs, std::make_pair(properties::prop_int, 1)) - .data<&data_type::h>("h"_hs, std::make_pair(properties::prop_int, 2)) - .data<&data_type::k>("k"_hs, std::make_pair(properties::prop_int, 3)) + .data<&data_type::i, entt::as_alias_t>("i"_hs) + .prop>() + .data<&data_type::j>("j"_hs) + .prop>() + .data<&data_type::h>("h"_hs) + .prop>() + .data<&data_type::k>("k"_hs) + .prop>() .data<&data_type::empty>("empty"_hs) .data<&data_type::v, entt::as_void_t>("v"_hs); @@ -193,11 +204,16 @@ struct Meta: ::testing::Test { entt::meta() .type("func"_hs) .func(&func_type::f)>("f3"_hs) - .func(&func_type::f)>("f2"_hs, std::make_pair(properties::prop_bool, false)) - .func(&func_type::f)>("f1"_hs, std::make_pair(properties::prop_bool, false)) - .func<&func_type::g>("g"_hs, std::make_pair(properties::prop_bool, false)) - .func<&func_type::h>("h"_hs, std::make_pair(properties::prop_bool, false)) - .func<&func_type::k>("k"_hs, std::make_pair(properties::prop_bool, false)) + .func(&func_type::f)>("f2"_hs) + .prop>() + .func(&func_type::f)>("f1"_hs) + .prop>() + .func<&func_type::g>("g"_hs) + .prop>() + .func<&func_type::h>("h"_hs) + .prop>() + .func<&func_type::k>("k"_hs) + .prop>() .func<&func_type::v, entt::as_void_t>("v"_hs) .func<&func_type::a, entt::as_alias_t>("a"_hs); @@ -209,13 +225,15 @@ struct Meta: ::testing::Test { .data<&setter_getter_type::setter_with_ref, &setter_getter_type::getter_with_ref>("w"_hs); entt::meta() - .type("an_abstract_type"_hs, std::make_pair(properties::prop_bool, false)) + .type("an_abstract_type"_hs) + .prop>() .data<&an_abstract_type::i>("i"_hs) .func<&an_abstract_type::f>("f"_hs) .func<&an_abstract_type::g>("g"_hs); entt::meta() - .type("another_abstract_type"_hs, std::make_pair(properties::prop_int, 42)) + .type("another_abstract_type"_hs) + .prop>() .data<&another_abstract_type::j>("j"_hs) .func<&another_abstract_type::h>("h"_hs); @@ -230,7 +248,8 @@ struct Meta: ::testing::Test { entt::meta().conv(); entt::meta() - .type("my_type"_hs, std::make_pair(properties::prop_bool, false)) + .type("my_type"_hs) + .prop>() .ctor<>(); entt::meta() @@ -1982,6 +2001,18 @@ TEST_F(Meta, Variables) { ASSERT_EQ(c, 'x'); } +TEST_F(Meta, SharedProperties) { + const auto type = entt::resolve(); + const auto prop_bool = type.data("prop_bool"_hs); + const auto prop_int = type.data("prop_int"_hs); + + ASSERT_TRUE(prop_bool.prop(properties::prop_int)); + ASSERT_FALSE(prop_bool.prop(properties::prop_bool)); + + ASSERT_TRUE(prop_int.prop(properties::prop_int)); + ASSERT_TRUE(prop_int.prop(properties::prop_bool)); +} + TEST_F(Meta, Reset) { ASSERT_NE(*entt::internal::meta_info<>::ctx, nullptr); ASSERT_NE(entt::internal::meta_info<>::local, nullptr);