Browse Source

meta:
* meta_prop_node is no longer static
* we can attach multiple properties at different times
* meta property keys are of type id_type only
* removed meta_factory::props (use prop instead)
* meta range returns id and meta object pairs now
* removed mera_prop::key (id returned by meta ranges)

Michele Caini 3 years ago
parent
commit
945e61d668

+ 28 - 108
src/entt/meta/factory.hpp

@@ -26,18 +26,6 @@ namespace entt {
 
 namespace internal {
 
-inline void link_prop_if_required(internal::meta_prop_node **ref, internal::meta_prop_node &node) noexcept {
-    for(auto it = *ref; it; it = it->next) {
-        if(it == &node) {
-            return;
-        }
-        ENTT_ASSERT(it->id != node.id, "Duplicate identifier");
-    }
-
-    node.next = *ref;
-    *ref = &node;
-}
-
 inline void link_type_if_required(meta_type_node *owner, const id_type id) noexcept {
     ENTT_ASSERT((owner->id = {}, !resolve(id)), "Duplicate identifier");
     owner->id = id;
@@ -82,95 +70,39 @@ class meta_factory;
 /**
  * @brief Extended meta factory to be used for reflection purposes.
  * @tparam Type Reflected type for which the factory was created.
- * @tparam Spec Property specialization pack used to disambiguate overloads.
+ * @tparam Prop Type of container to store properties.
  */
-template<typename Type, typename... Spec>
-class meta_factory<Type, Spec...>: public meta_factory<Type> {
-    template<std::size_t Step = 0, typename... Property, typename... Other>
-    void unroll(choice_t<2>, std::tuple<Property...> property, Other &&...other) noexcept {
-        std::apply([this](auto &&...curr) { (this->unroll<Step>(choice<2>, std::forward<Property>(curr)...)); }, property);
-        unroll<Step + sizeof...(Property)>(choice<2>, std::forward<Other>(other)...);
-    }
-
-    template<std::size_t Step = 0, typename... Property, typename... Other>
-    void unroll(choice_t<1>, std::pair<Property...> property, Other &&...other) noexcept {
-        assign<Step>(std::move(property.first), std::move(property.second));
-        unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
-    }
-
-    template<std::size_t Step = 0, typename Property, typename... Other>
-    void unroll(choice_t<0>, Property &&property, Other &&...other) noexcept {
-        assign<Step>(std::forward<Property>(property));
-        unroll<Step + 1>(choice<2>, std::forward<Other>(other)...);
-    }
-
-    template<std::size_t>
-    void unroll(choice_t<0>) noexcept {}
-
-    template<std::size_t = 0>
-    void assign(meta_any key, meta_any value = {}) {
-        static meta_any property[2u]{};
-
-        static internal::meta_prop_node node{
-            nullptr,
-            property[0u],
-            property[1u]
-            // tricks clang-format
-        };
-
-        property[0u] = std::move(key);
-        property[1u] = std::move(value);
-
-        internal::link_prop_if_required(ref, node);
-    }
-
+template<typename Type, typename Prop>
+class meta_factory<Type, Prop>: public meta_factory<Type> {
 public:
     /**
      * @brief Constructs an extended factory from a given node.
-     * @param target The underlying node to which to assign the properties.
+     * @param target The underlying container to store properties.
      */
-    meta_factory(internal::meta_prop_node **target) noexcept
-        : ref{target} {}
+    meta_factory(Prop &target) noexcept
+        : container{&target} {}
 
     /**
      * @brief Assigns a property to the last meta object created.
      *
      * Both the key and the value (if any) must be at least copy constructible.
      *
-     * @tparam PropertyOrKey Type of the property or property key.
      * @tparam Value Optional type of the property value.
-     * @param property_or_key Property or property key.
+     * @param key Property key.
      * @param value Optional property value.
-     * @return A meta factory for the parent type.
+     * @return An extended meta factory for the given type.
      */
-    template<typename PropertyOrKey, typename... Value>
-    meta_factory<Type> prop(PropertyOrKey &&property_or_key, Value &&...value) {
-        if constexpr(sizeof...(Value) == 0) {
-            unroll(choice<2>, std::forward<PropertyOrKey>(property_or_key));
-        } else {
-            assign(std::forward<PropertyOrKey>(property_or_key), std::forward<Value>(value)...);
-        }
-
-        return {};
-    }
+    template<typename... Value>
+    meta_factory prop(id_type key, Value &&...value) {
+        (*container)[key] = internal::meta_prop_node{
+            internal::meta_node<std::decay_t<Value>>::resolve()...,
+            std::forward<Value>(value)...};
 
-    /**
-     * @brief Assigns properties to the last meta object created.
-     *
-     * Both key and value (if any) must be at least copy constructible.
-     *
-     * @tparam Property Types of the properties.
-     * @param property Properties to assign to the last meta object created.
-     * @return A meta factory for the parent type.
-     */
-    template<typename... Property>
-    meta_factory<Type> props(Property... property) {
-        unroll(choice<2>, std::forward<Property>(property)...);
-        return {};
+        return *this;
     }
 
 private:
-    internal::meta_prop_node **ref;
+    Prop *container;
 };
 
 /**
@@ -188,14 +120,13 @@ class meta_factory<Type> {
         owner->data[id] = internal::meta_data_node{
             /* this is never static */
             (std::is_member_object_pointer_v<decltype(value_list_element_v<Index, Setter>)> && ... && std::is_const_v<std::remove_reference_t<data_type>>) ? internal::meta_traits::is_const : internal::meta_traits::is_none,
-            nullptr,
             Setter::size,
             internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
             &meta_arg<type_list<type_list_element_t<type_list_element_t<Index, args_type>::size != 1u, type_list_element_t<Index, args_type>>...>>,
             [](meta_handle instance, meta_any value) -> bool { return (meta_setter<Type, value_list_element_v<Index, Setter>>(*instance.operator->(), value.as_ref()) || ...); },
             &meta_getter<Type, Getter, Policy>};
 
-        return meta_factory<Type, Setter, std::integral_constant<decltype(Getter), Getter>>{&owner->data[id].prop};
+        return meta_factory<Type, decltype(internal::meta_data_node::prop)>{owner->data[id].prop};
     }
 
 public:
@@ -210,7 +141,7 @@ public:
      */
     auto type(const id_type id = type_hash<Type>::value()) noexcept {
         internal::link_type_if_required(owner, id);
-        return meta_factory<Type, Type>{&owner->prop};
+        return meta_factory<Type, decltype(internal::meta_type_node::prop)>{owner->prop};
     }
 
     /**
@@ -378,27 +309,25 @@ public:
             owner->data[id] = internal::meta_data_node{
                 /* this is never static */
                 std::is_const_v<data_type> ? internal::meta_traits::is_const : internal::meta_traits::is_none,
-                nullptr,
                 1u,
                 internal::meta_node<std::remove_const_t<data_type>>::resolve(),
                 &meta_arg<type_list<std::remove_const_t<data_type>>>,
                 &meta_setter<Type, Data>,
                 &meta_getter<Type, Data, Policy>};
 
-            return meta_factory<Type, std::integral_constant<decltype(Data), Data>, std::integral_constant<decltype(Data), Data>>{&owner->data[id].prop};
+            return meta_factory<Type, decltype(internal::meta_data_node::prop)>{owner->data[id].prop};
         } else {
             using data_type = std::remove_reference_t<std::remove_pointer_t<decltype(Data)>>;
 
             owner->data[id] = internal::meta_data_node{
                 ((std::is_same_v<Type, std::remove_const_t<data_type>> || std::is_const_v<data_type>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static,
-                nullptr,
                 1u,
                 internal::meta_node<std::remove_const_t<data_type>>::resolve(),
                 &meta_arg<type_list<std::remove_const_t<data_type>>>,
                 &meta_setter<Type, Data>,
                 &meta_getter<Type, Data, Policy>};
 
-            return meta_factory<Type, std::integral_constant<decltype(Data), Data>>{&owner->data[id].prop};
+            return meta_factory<Type, decltype(internal::meta_data_node::prop)>{owner->data[id].prop};
         }
     }
 
@@ -431,28 +360,26 @@ public:
             owner->data[id] = internal::meta_data_node{
                 /* this is never static */
                 internal::meta_traits::is_const,
-                nullptr,
                 0u,
                 internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
                 &meta_arg<type_list<>>,
                 &meta_setter<Type, Setter>,
                 &meta_getter<Type, Getter, Policy>};
 
-            return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&owner->data[id].prop};
+            return meta_factory<Type, decltype(internal::meta_data_node::prop)>{owner->data[id].prop};
         } else {
             using args_type = typename meta_function_helper_t<Type, decltype(Setter)>::args_type;
 
             owner->data[id] = internal::meta_data_node{
                 /* this is never static nor const */
                 internal::meta_traits::is_none,
-                nullptr,
                 1u,
                 internal::meta_node<std::remove_cv_t<std::remove_reference_t<data_type>>>::resolve(),
                 &meta_arg<type_list<type_list_element_t<args_type::size != 1u, args_type>>>,
                 &meta_setter<Type, Setter>,
                 &meta_getter<Type, Getter, Policy>};
 
-            return meta_factory<Type, std::integral_constant<decltype(Setter), Setter>, std::integral_constant<decltype(Getter), Getter>>{&owner->data[id].prop};
+            return meta_factory<Type, decltype(internal::meta_data_node::prop)>{owner->data[id].prop};
         }
     }
 
@@ -500,7 +427,6 @@ public:
             {},
             (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none),
             nullptr,
-            nullptr,
             descriptor::args_type::size,
             internal::meta_node<std::conditional_t<std::is_same_v<Policy, as_void_t>, void, std::remove_cv_t<std::remove_reference_t<typename descriptor::return_type>>>>::resolve(),
             &meta_arg<typename descriptor::args_type>,
@@ -509,7 +435,7 @@ public:
         };
 
         internal::link_func_if_required(owner, id, node);
-        return meta_factory<Type, std::integral_constant<decltype(Candidate), Candidate>>{&node.prop};
+        return meta_factory<Type, decltype(internal::meta_func_node::prop)>{node.prop};
     }
 
 private:
@@ -531,7 +457,7 @@ template<typename Type>
 [[nodiscard]] auto meta() noexcept {
     auto *const node = internal::meta_node<Type>::resolve();
     // extended meta factory to allow assigning properties to opaque meta types
-    return meta_factory<Type, Type>{&node->prop};
+    return meta_factory<Type, decltype(internal::meta_type_node::prop)>{node->prop};
 }
 
 /**
@@ -546,26 +472,20 @@ template<typename Type>
  * @param id Unique identifier.
  */
 inline void meta_reset(const id_type id) noexcept {
-    auto clear_chain = [](auto **curr, auto... member) {
-        for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
-            if constexpr(sizeof...(member) != 0u) {
-                static_assert(sizeof...(member) == 1u, "Assert in defense of the future me");
-                for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {}
-            }
-        }
-    };
-
     for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) {
         if(auto *node = *it; node->id == id) {
-            clear_chain(&node->prop);
-            clear_chain(&node->func, &internal::meta_func_node::prop);
+            for(auto **curr = &node->func; *curr; *curr = std::exchange((*curr)->next, nullptr)) {
+                (*curr)->prop.clear();
+            }
 
             node->id = {};
+            node->prop.clear();
             node->ctor.clear();
             node->base.clear();
             node->conv.clear();
             node->data.clear();
             node->dtor.dtor = nullptr;
+
             *it = std::exchange(node->next, nullptr);
 
             break;

+ 25 - 27
src/entt/meta/meta.hpp

@@ -664,20 +664,12 @@ struct meta_prop {
     meta_prop(const node_type *curr = nullptr) noexcept
         : node{curr} {}
 
-    /**
-     * @brief Returns the stored key as a const reference.
-     * @return A wrapper containing the key stored with the property.
-     */
-    [[nodiscard]] meta_any key() const {
-        return node->id.as_ref();
-    }
-
     /**
      * @brief Returns the stored value by copy.
      * @return A wrapper containing the value stored with the property.
      */
     [[nodiscard]] meta_any value() const {
-        return node->value;
+        return (node && node->type) ? node->type->from_void(nullptr, node->value.data()) : meta_any{};
     }
 
     /**
@@ -772,8 +764,8 @@ struct meta_data {
      * @brief Returns a range to visit registered meta properties.
      * @return An iterable range to visit registered meta properties.
      */
-    [[nodiscard]] old_meta_range<meta_prop> prop() const noexcept {
-        return {node->prop, nullptr};
+    [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_data_node::prop)::const_iterator> prop() const noexcept {
+        return {node->prop.cbegin(), node->prop.cend()};
     }
 
     /**
@@ -781,11 +773,9 @@ struct meta_data {
      * @param key The key to use to search for a property.
      * @return The registered meta property for the given key, if any.
      */
-    [[nodiscard]] meta_prop prop(meta_any key) const {
-        for(auto curr: prop()) {
-            if(curr.key() == key) {
-                return curr;
-            }
+    [[nodiscard]] meta_prop prop(const id_type key) const {
+        if(auto it = node->prop.find(key); it != node->prop.cend()) {
+            return &it->second;
         }
 
         return nullptr;
@@ -891,8 +881,8 @@ struct meta_func {
     }
 
     /*! @copydoc meta_data::prop */
-    [[nodiscard]] old_meta_range<meta_prop> prop() const noexcept {
-        return {node->prop, nullptr};
+    [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_func_node::prop)::const_iterator> prop() const noexcept {
+        return {node->prop.cbegin(), node->prop.cend()};
     }
 
     /**
@@ -900,11 +890,9 @@ struct meta_func {
      * @param key The key to use to search for a property.
      * @return The registered meta property for the given key, if any.
      */
-    [[nodiscard]] meta_prop prop(meta_any key) const {
-        for(auto curr: prop()) {
-            if(curr.key() == key) {
-                return curr;
-            }
+    [[nodiscard]] meta_prop prop(const id_type key) const {
+        if(auto it = node->prop.find(key); it != node->prop.cend()) {
+            return &it->second;
         }
 
         return nullptr;
@@ -1364,8 +1352,8 @@ public:
      * @brief Returns a range to visit registered top-level meta properties.
      * @return An iterable range to visit registered top-level meta properties.
      */
-    [[nodiscard]] old_meta_range<meta_prop> prop() const noexcept {
-        return {node->prop, nullptr};
+    [[nodiscard]] meta_range<meta_prop, typename decltype(internal::meta_type_node::prop)::const_iterator> prop() const noexcept {
+        return {node->prop.cbegin(), node->prop.cend()};
     }
 
     /**
@@ -1376,8 +1364,18 @@ public:
      * @param key The key to use to search for a property.
      * @return The registered meta property for the given key, if any.
      */
-    [[nodiscard]] meta_prop prop(meta_any key) const {
-        return internal::find_by<&internal::meta_type_node::prop>(key, node);
+    [[nodiscard]] meta_prop prop(const id_type key) const {
+        if(auto it = node->prop.find(key); it != node->prop.cend()) {
+            return &it->second;
+        }
+
+        for(auto &&curr: base()) {
+            if(auto elem = curr.second.prop(key); elem) {
+                return elem;
+            }
+        }
+
+        return nullptr;
     }
 
     /**

+ 6 - 8
src/entt/meta/node.hpp

@@ -46,9 +46,8 @@ enum class meta_traits : std::uint32_t {
 struct meta_type_node;
 
 struct meta_prop_node {
-    meta_prop_node *next;
-    const meta_any &id;
-    meta_any &value;
+    meta_type_node *type{nullptr};
+    basic_any<0u> value{};
 };
 
 struct meta_base_node {
@@ -76,12 +75,12 @@ struct meta_data_node {
     using size_type = std::size_t;
 
     meta_traits traits;
-    meta_prop_node *prop;
     size_type arity;
     meta_type_node *type;
     meta_type (*arg)(const size_type) noexcept;
     bool (*set)(meta_handle, meta_any);
     meta_any (*get)(meta_handle);
+    dense_map<id_type, meta_prop_node, identity> prop{};
 };
 
 struct meta_func_node {
@@ -90,11 +89,11 @@ struct meta_func_node {
     id_type id;
     const meta_traits traits;
     meta_func_node *next;
-    meta_prop_node *prop;
     const size_type arity;
     meta_type_node *const ret;
     meta_type (*const arg)(const size_type) noexcept;
     meta_any (*const invoke)(meta_handle, meta_any *const);
+    dense_map<id_type, meta_prop_node, identity> prop{};
 };
 
 struct meta_template_node {
@@ -112,13 +111,13 @@ struct meta_type_node {
     id_type id;
     const meta_traits traits;
     meta_type_node *next;
-    meta_prop_node *prop;
     const size_type size_of;
     meta_type_node *(*const remove_pointer)() noexcept;
     meta_any (*const default_constructor)();
     double (*const conversion_helper)(void *, const void *);
     meta_any (*const from_void)(void *, const void *);
     meta_template_node templ;
+    dense_map<id_type, meta_prop_node, identity> prop{};
     dense_map<id_type, meta_ctor_node, identity> ctor{};
     dense_map<id_type, meta_base_node, identity> base{};
     dense_map<id_type, meta_conv_node, identity> conv{};
@@ -132,7 +131,7 @@ meta_type_node *meta_arg_node(type_list<Args...>, const std::size_t index) noexc
 
 template<typename Type>
 class ENTT_API meta_node {
-    static_assert(std::is_same_v<Type, std::remove_cv_t<std::remove_reference_t<Type>>>, "Invalid type");
+    static_assert(std::is_same_v<Type, std::remove_const_t<std::remove_reference_t<Type>>>, "Invalid type");
 
     [[nodiscard]] static auto *meta_default_constructor() noexcept {
         if constexpr(std::is_default_constructible_v<Type>) {
@@ -195,7 +194,6 @@ public:
                 | (is_complete_v<meta_sequence_container_traits<Type>> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none)
                 | (is_complete_v<meta_associative_container_traits<Type>> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none),
             nullptr,
-            nullptr,
             size_of_v<Type>,
             &meta_node<std::remove_cv_t<std::remove_pointer_t<Type>>>::resolve,
             meta_default_constructor(),

+ 19 - 23
test/entt/meta/meta_data.cpp

@@ -93,7 +93,7 @@ struct array_t {
     int local[5];
 };
 
-enum class property_t {
+enum class property_t : entt::id_type {
     random,
     value
 };
@@ -119,14 +119,14 @@ struct MetaData: ::testing::Test {
         entt::meta<clazz_t>()
             .type("clazz"_hs)
             .data<&clazz_t::i, entt::as_ref_t>("i"_hs)
-            .prop(3, 0)
+            .prop(3u, 0)
             .data<&clazz_t::i, entt::as_cref_t>("ci"_hs)
             .data<&clazz_t::j>("j"_hs)
-            .prop(true, 1)
+            .prop("true"_hs, 1)
             .data<&clazz_t::h>("h"_hs)
-            .prop(property_t::random, 2)
+            .prop(static_cast<entt::id_type>(property_t::random), 2)
             .data<&clazz_t::k>("k"_hs)
-            .prop(property_t::value, 3)
+            .prop(static_cast<entt::id_type>(property_t::value), 3)
             .data<&clazz_t::base>("base"_hs)
             .data<&clazz_t::i, entt::as_void_t>("void"_hs)
             .conv<int>();
@@ -176,17 +176,16 @@ TEST_F(MetaData, Functionalities) {
     ASSERT_EQ(data.get(instance).cast<int>(), 42);
 
     for(auto curr: data.prop()) {
-        ASSERT_EQ(curr.key(), 3);
-        ASSERT_EQ(curr.value(), 0);
+        ASSERT_EQ(curr.first, 3u);
+        ASSERT_EQ(curr.second.value(), 0);
     }
 
     ASSERT_FALSE(data.prop(2));
     ASSERT_FALSE(data.prop('c'));
 
-    auto prop = data.prop(3);
+    auto prop = data.prop(3u);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), 3);
     ASSERT_EQ(prop.value(), 0);
 }
 
@@ -207,17 +206,16 @@ TEST_F(MetaData, Const) {
     ASSERT_EQ(data.get(instance).cast<int>(), 1);
 
     for(auto curr: data.prop()) {
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_EQ(curr.value(), 1);
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_EQ(curr.second.value(), 1);
     }
 
     ASSERT_FALSE(data.prop(false));
     ASSERT_FALSE(data.prop('c'));
 
-    auto prop = data.prop(true);
+    auto prop = data.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_EQ(prop.value(), 1);
 }
 
@@ -237,17 +235,16 @@ TEST_F(MetaData, Static) {
     ASSERT_EQ(data.get({}).cast<int>(), 42);
 
     for(auto curr: data.prop()) {
-        ASSERT_EQ(curr.key(), property_t::random);
-        ASSERT_EQ(curr.value(), 2);
+        ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::random));
+        ASSERT_EQ(curr.second.value(), 2);
     }
 
-    ASSERT_FALSE(data.prop(property_t::value));
+    ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::value)));
     ASSERT_FALSE(data.prop('c'));
 
-    auto prop = data.prop(property_t::random);
+    auto prop = data.prop(static_cast<entt::id_type>(property_t::random));
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), property_t::random);
     ASSERT_EQ(prop.value(), 2);
 }
 
@@ -267,17 +264,16 @@ TEST_F(MetaData, ConstStatic) {
     ASSERT_EQ(data.get({}).cast<int>(), 3);
 
     for(auto curr: data.prop()) {
-        ASSERT_EQ(curr.key(), property_t::value);
-        ASSERT_EQ(curr.value(), 3);
+        ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
+        ASSERT_EQ(curr.second.value(), 3);
     }
 
-    ASSERT_FALSE(data.prop(property_t::random));
+    ASSERT_FALSE(data.prop(static_cast<entt::id_type>(property_t::random)));
     ASSERT_FALSE(data.prop('c'));
 
-    auto prop = data.prop(property_t::value);
+    auto prop = data.prop(static_cast<entt::id_type>(property_t::value));
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), property_t::value);
     ASSERT_EQ(prop.value(), 3);
 }
 

+ 20 - 26
test/entt/meta/meta_func.cpp

@@ -114,15 +114,15 @@ struct MetaFunc: ::testing::Test {
             .func<&entt::registry::emplace_or_replace<func_t>>("emplace"_hs)
             .func<entt::overload<int(const base_t &, int, int)>(&func_t::f)>("f3"_hs)
             .func<entt::overload<int(int, int)>(&func_t::f)>("f2"_hs)
-            .prop(true, false)
+            .prop("true"_hs, false)
             .func<entt::overload<int(int) const>(&func_t::f)>("f1"_hs)
-            .prop(true, false)
+            .prop("true"_hs, false)
             .func<&func_t::g>("g"_hs)
-            .prop(true, false)
+            .prop("true"_hs, false)
             .func<func_t::h>("h"_hs)
-            .prop(true, false)
+            .prop("true"_hs, false)
             .func<func_t::k>("k"_hs)
-            .prop(true, false)
+            .prop("true"_hs, false)
             .func<&func_t::v, entt::as_void_t>("v"_hs)
             .func<&func_t::a, entt::as_ref_t>("a"_hs)
             .func<&func_t::a, entt::as_cref_t>("ca"_hs)
@@ -180,17 +180,16 @@ TEST_F(MetaFunc, Functionalities) {
     ASSERT_EQ(func_t::value, 3);
 
     for(auto curr: func.prop()) {
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_FALSE(curr.value().template cast<bool>());
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_FALSE(curr.second.value().template cast<bool>());
     }
 
     ASSERT_FALSE(func.prop(false));
     ASSERT_FALSE(func.prop('c'));
 
-    auto prop = func.prop(true);
+    auto prop = func.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 
@@ -218,17 +217,16 @@ TEST_F(MetaFunc, Const) {
     ASSERT_EQ(any.cast<int>(), 16);
 
     for(auto curr: func.prop()) {
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_FALSE(curr.value().template cast<bool>());
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_FALSE(curr.second.value().template cast<bool>());
     }
 
     ASSERT_FALSE(func.prop(false));
     ASSERT_FALSE(func.prop('c'));
 
-    auto prop = func.prop(true);
+    auto prop = func.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 
@@ -254,17 +252,16 @@ TEST_F(MetaFunc, RetVoid) {
     ASSERT_EQ(func_t::value, 25);
 
     for(auto curr: func.prop()) {
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_FALSE(curr.value().template cast<bool>());
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_FALSE(curr.second.value().template cast<bool>());
     }
 
     ASSERT_FALSE(func.prop(false));
     ASSERT_FALSE(func.prop('c'));
 
-    auto prop = func.prop(true);
+    auto prop = func.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 
@@ -292,17 +289,16 @@ TEST_F(MetaFunc, Static) {
     ASSERT_EQ(any.cast<int>(), 6);
 
     for(auto curr: func.prop()) {
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_FALSE(curr.value().template cast<bool>());
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_FALSE(curr.second.value().template cast<bool>());
     }
 
     ASSERT_FALSE(func.prop(false));
     ASSERT_FALSE(func.prop('c'));
 
-    auto prop = func.prop(true);
+    auto prop = func.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 
@@ -327,18 +323,16 @@ TEST_F(MetaFunc, StaticRetVoid) {
     ASSERT_EQ(func_t::value, 42);
 
     for(auto curr: func.prop()) {
-        ASSERT_TRUE(curr);
-        ASSERT_EQ(curr.key(), true);
-        ASSERT_FALSE(curr.value().template cast<bool>());
+        ASSERT_EQ(curr.first, "true"_hs);
+        ASSERT_FALSE(curr.second.value().template cast<bool>());
     }
 
     ASSERT_FALSE(func.prop(false));
     ASSERT_FALSE(func.prop('c'));
 
-    auto prop = func.prop(true);
+    auto prop = func.prop("true"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), true);
     ASSERT_FALSE(prop.value().cast<bool>());
 }
 

+ 11 - 9
test/entt/meta/meta_prop.cpp

@@ -22,11 +22,13 @@ struct MetaProp: ::testing::Test {
 
         entt::meta<base_2_t>()
             .type("base_2"_hs)
-            .props(std::make_pair("bool"_hs, false), std::make_pair("char[]"_hs, "char[]"));
+            .prop("bool"_hs, false)
+            .prop("char[]"_hs, "char[]");
 
         entt::meta<base_3_t>()
             .type("base_3"_hs)
-            .prop(std::make_tuple("key_only"_hs, std::make_pair("key"_hs, 42)));
+            .prop("key_only"_hs)
+            .prop("key"_hs, 42);
 
         entt::meta<derived_t>()
             .type("derived"_hs)
@@ -46,7 +48,6 @@ TEST_F(MetaProp, Functionalities) {
     auto prop = entt::resolve<base_1_t>().prop("int"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), "int"_hs);
     ASSERT_EQ(prop.value(), 42);
 }
 
@@ -76,7 +77,6 @@ TEST_F(MetaProp, DeducedArrayType) {
     auto prop = entt::resolve<base_2_t>().prop("char[]"_hs);
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), "char[]"_hs);
     ASSERT_EQ(prop.value().type(), entt::resolve<const char *>());
     ASSERT_EQ(strcmp(prop.value().cast<const char *>(), "char[]"), 0);
 }
@@ -89,18 +89,20 @@ TEST_F(MetaProp, ReRegistration) {
     auto *node = entt::internal::meta_node<base_1_t>::resolve();
     auto type = entt::resolve<base_1_t>();
 
-    ASSERT_NE(node->prop, nullptr);
-    ASSERT_EQ(node->prop->next, nullptr);
+    ASSERT_FALSE(node->prop.empty());
+    ASSERT_EQ(node->prop.size(), 1u);
 
     ASSERT_TRUE(type.prop("int"_hs));
     ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 42);
 
+    entt::meta<base_1_t>().prop("int"_hs, 0);
     entt::meta<base_1_t>().prop("double"_hs, 3.);
 
-    ASSERT_NE(node->prop, nullptr);
-    ASSERT_EQ(node->prop->next, nullptr);
+    ASSERT_FALSE(node->prop.empty());
+    ASSERT_EQ(node->prop.size(), 2u);
 
-    ASSERT_FALSE(type.prop("int"_hs));
+    ASSERT_TRUE(type.prop("int"_hs));
     ASSERT_TRUE(type.prop("double"_hs));
+    ASSERT_EQ(type.prop("int"_hs).value().cast<int>(), 0);
     ASSERT_EQ(type.prop("double"_hs).value().cast<double>(), 3.);
 }

+ 35 - 29
test/entt/meta/meta_type.cpp

@@ -100,7 +100,7 @@ struct overloaded_func_t {
     inline static int value = 0;
 };
 
-enum class property_t {
+enum class property_t : entt::id_type {
     random,
     value,
     key_only,
@@ -150,18 +150,24 @@ struct MetaType: ::testing::Test {
         entt::meta<property_t>()
             .type("property"_hs)
             .data<property_t::random>("random"_hs)
-            .props(std::make_pair(property_t::random, 0), std::make_pair(property_t::value, 3))
+            .prop(static_cast<entt::id_type>(property_t::random), 0)
+            .prop(static_cast<entt::id_type>(property_t::value), 3)
             .data<property_t::value>("value"_hs)
-            .props(std::make_pair(property_t::random, true), std::make_pair(property_t::value, 0), property_t::key_only, property_t::list)
+            .prop(static_cast<entt::id_type>(property_t::random), true)
+            .prop(static_cast<entt::id_type>(property_t::value), 0)
+            .prop(static_cast<entt::id_type>(property_t::key_only))
+            .prop(static_cast<entt::id_type>(property_t::list))
             .data<property_t::key_only>("key_only"_hs)
-            .prop(property_t::key_only)
+            .prop(static_cast<entt::id_type>(property_t::key_only))
             .data<property_t::list>("list"_hs)
-            .props(std::make_pair(property_t::random, false), std::make_pair(property_t::value, 0), property_t::key_only)
+            .prop(static_cast<entt::id_type>(property_t::random), false)
+            .prop(static_cast<entt::id_type>(property_t::value), 0)
+            .prop(static_cast<entt::id_type>(property_t::key_only))
             .data<set<property_t>, get<property_t>>("var"_hs);
 
         entt::meta<clazz_t>()
             .type("clazz"_hs)
-            .prop(property_t::value, 42)
+            .prop(static_cast<entt::id_type>(property_t::value), 42)
             .ctor<const base_t &, int>()
             .data<&clazz_t::value>("value"_hs)
             .func<&clazz_t::member>("member"_hs)
@@ -210,17 +216,16 @@ TEST_F(MetaType, Functionalities) {
     ASSERT_EQ(type.info(), entt::type_id<clazz_t>());
 
     for(auto curr: type.prop()) {
-        ASSERT_EQ(curr.key(), property_t::value);
-        ASSERT_EQ(curr.value(), 42);
+        ASSERT_EQ(curr.first, static_cast<entt::id_type>(property_t::value));
+        ASSERT_EQ(curr.second.value(), 42);
     }
 
-    ASSERT_FALSE(type.prop(property_t::key_only));
+    ASSERT_FALSE(type.prop(static_cast<entt::id_type>(property_t::key_only)));
     ASSERT_FALSE(type.prop("property"_hs));
 
-    auto prop = type.prop(property_t::value);
+    auto prop = type.prop(static_cast<entt::id_type>(property_t::value));
 
     ASSERT_TRUE(prop);
-    ASSERT_EQ(prop.key(), property_t::value);
     ASSERT_EQ(prop.value(), 42);
 }
 
@@ -498,7 +503,7 @@ TEST_F(MetaType, Reset) {
 
     ASSERT_TRUE(entt::resolve("clazz"_hs));
     ASSERT_EQ(entt::resolve<clazz_t>().id(), "clazz"_hs);
-    ASSERT_TRUE(entt::resolve<clazz_t>().prop(property_t::value));
+    ASSERT_TRUE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
     ASSERT_TRUE(entt::resolve<clazz_t>().data("value"_hs));
     ASSERT_TRUE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
     // implicitly generated default constructor
@@ -508,7 +513,7 @@ TEST_F(MetaType, Reset) {
 
     ASSERT_FALSE(entt::resolve("clazz"_hs));
     ASSERT_NE(entt::resolve<clazz_t>().id(), "clazz"_hs);
-    ASSERT_FALSE(entt::resolve<clazz_t>().prop(property_t::value));
+    ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
     ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
     ASSERT_FALSE((entt::resolve<clazz_t>().construct(derived_t{}, clazz_t{})));
     // implicitly generated default constructor is not cleared
@@ -612,21 +617,21 @@ TEST_F(MetaType, PropertiesAndCornerCases) {
 
     auto type = entt::resolve<property_t>();
 
-    ASSERT_EQ(type.data("random"_hs).prop(property_t::random).value().cast<int>(), 0);
-    ASSERT_EQ(type.data("random"_hs).prop(property_t::value).value().cast<int>(), 3);
+    ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<int>(), 0);
+    ASSERT_EQ(type.data("random"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 3);
 
-    ASSERT_EQ(type.data("value"_hs).prop(property_t::random).value().cast<bool>(), true);
-    ASSERT_EQ(type.data("value"_hs).prop(property_t::value).value().cast<int>(), 0);
-    ASSERT_TRUE(type.data("value"_hs).prop(property_t::key_only));
-    ASSERT_FALSE(type.data("value"_hs).prop(property_t::key_only).value());
+    ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), true);
+    ASSERT_EQ(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
+    ASSERT_TRUE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
+    ASSERT_FALSE(type.data("value"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
 
-    ASSERT_TRUE(type.data("key_only"_hs).prop(property_t::key_only));
-    ASSERT_FALSE(type.data("key_only"_hs).prop(property_t::key_only).value());
+    ASSERT_TRUE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
+    ASSERT_FALSE(type.data("key_only"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
 
-    ASSERT_EQ(type.data("list"_hs).prop(property_t::random).value().cast<bool>(), false);
-    ASSERT_EQ(type.data("list"_hs).prop(property_t::value).value().cast<int>(), 0);
-    ASSERT_TRUE(type.data("list"_hs).prop(property_t::key_only));
-    ASSERT_FALSE(type.data("list"_hs).prop(property_t::key_only).value());
+    ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::random)).value().cast<bool>(), false);
+    ASSERT_EQ(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::value)).value().cast<int>(), 0);
+    ASSERT_TRUE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)));
+    ASSERT_FALSE(type.data("list"_hs).prop(static_cast<entt::id_type>(property_t::key_only)).value());
 }
 
 TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
@@ -651,7 +656,7 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
 
     ASSERT_EQ(*entt::internal::meta_context::global(), nullptr);
 
-    ASSERT_FALSE(entt::resolve<clazz_t>().prop(property_t::value));
+    ASSERT_FALSE(entt::resolve<clazz_t>().prop(static_cast<entt::id_type>(property_t::value)));
     // implicitly generated default constructor is not cleared
     ASSERT_TRUE(entt::resolve<clazz_t>().construct());
     ASSERT_FALSE(entt::resolve<clazz_t>().data("value"_hs));
@@ -670,10 +675,11 @@ TEST_F(MetaType, ResetAndReRegistrationAfterReset) {
     entt::meta<property_t>()
         .type("property"_hs)
         .data<property_t::random>("rand"_hs)
-        .props(std::make_pair(property_t::value, 42), std::make_pair(property_t::random, 3));
+        .prop(static_cast<entt::id_type>(property_t::value), 42)
+        .prop(static_cast<entt::id_type>(property_t::random), 3);
 
-    ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(property_t::value));
-    ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(property_t::random));
+    ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::value)));
+    ASSERT_TRUE(entt::resolve<property_t>().data("rand"_hs).prop(static_cast<entt::id_type>(property_t::random)));
 }
 
 TEST_F(MetaType, ReRegistration) {