diff --git a/TODO b/TODO index 629edac71..8f45c5408 100644 --- a/TODO +++ b/TODO @@ -19,4 +19,7 @@ * review sparse set to allow customization (mix pack in the spec, base is position only) - non-owning groups can iterate pages and skip empty ones, this should mitigate the lack of the packed array * review 64 bit id: user defined area + dedicated member on the registry to set it +* early out in views using bitmasks with bloom filter like access based on modulus + - standard each, use bitmask to speed up the whole thing and avoid accessing the pools to test for the page + - iterator based each with a couple of iterators passed from outside (use bitmask + has) * reactive systems diff --git a/src/entt/meta/factory.hpp b/src/entt/meta/factory.hpp index 93ee02585..6c9d10c6d 100644 --- a/src/entt/meta/factory.hpp +++ b/src/entt/meta/factory.hpp @@ -39,20 +39,6 @@ template class meta_factory { static_assert(std::is_object_v && !(std::is_const_v || std::is_volatile_v)); - template - static std::enable_if_t, decltype(std::declval().*Data)> - actual_type(); - - template - static std::enable_if_t, decltype(*Data)> - actual_type(); - - template - using data_type = std::remove_reference_t())>; - - template - using func_type = internal::meta_function_helper>; - template inline bool duplicate(const hashed_string &name, const Node *node) ENTT_NOEXCEPT { return node ? node->name == name || duplicate(name, node->next) : false; @@ -69,7 +55,7 @@ class meta_factory { template internal::meta_prop_node * properties(Property &&property, Other &&... other) { - static std::decay_t prop{}; + static std::remove_cv_t> prop{}; static internal::meta_prop_node node{ nullptr, @@ -99,6 +85,7 @@ class meta_factory { std::is_void_v, std::is_integral_v, std::is_floating_point_v, + std::is_array_v, std::is_enum_v, std::is_union_v, std::is_class_v, @@ -106,6 +93,7 @@ class meta_factory { std::is_function_v, std::is_member_object_pointer_v, std::is_member_function_pointer_v, + std::extent_v, []() -> meta_type { return internal::meta_info>::resolve(); }, @@ -248,7 +236,7 @@ public: */ template meta_factory conv() ENTT_NOEXCEPT { - static_assert(std::is_convertible_v>); + static_assert(std::is_convertible_v); auto * const type = internal::meta_info::resolve(); static internal::meta_conv_node node{ @@ -257,7 +245,7 @@ public: nullptr, &internal::meta_info::resolve, [](void *instance) -> meta_any { - return static_cast>(*static_cast(instance)); + return static_cast(*static_cast(instance)); }, []() -> meta_conv { return &node; @@ -428,8 +416,8 @@ public: true, true, &internal::meta_info::resolve, - [](meta_handle, meta_any &) { return false; }, - [](meta_handle) -> meta_any { return Data; }, + [](meta_handle, meta_any, meta_any) { return false; }, + [](meta_handle, meta_any) -> meta_any { return Data; }, []() -> meta_data { return &node; } @@ -437,17 +425,41 @@ public: node.prop = properties>(std::forward(property)...); curr = &node; - } else { + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t().*Data)>; + static internal::meta_data_node node{ &internal::meta_info::template data, {}, type, nullptr, nullptr, - std::is_const_v>, + std::is_const_v, !std::is_member_object_pointer_v, - &internal::meta_info>::resolve, - &internal::setter>, Type, Data>, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, + &internal::getter, + []() -> meta_data { + return &node; + } + }; + + node.prop = properties>(std::forward(property)...); + curr = &node; + } else { + static_assert(std::is_pointer_v); + using data_type = std::remove_pointer_t; + + static internal::meta_data_node node{ + &internal::meta_info::template data, + {}, + type, + nullptr, + nullptr, + std::is_const_v, + !std::is_member_object_pointer_v, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, &internal::getter, []() -> meta_data { return &node; @@ -540,6 +552,7 @@ public: template meta_factory func(const char *str, Property &&... property) ENTT_NOEXCEPT { using owner_type = std::integral_constant; + using func_type = internal::meta_function_helper>; auto * const type = internal::meta_info::resolve(); static internal::meta_func_node node{ @@ -548,13 +561,13 @@ public: type, nullptr, nullptr, - func_type::size, - func_type::is_const, - func_type::is_static, - &internal::meta_info::return_type>::resolve, - &func_type::arg, + func_type::size, + func_type::is_const, + func_type::is_static, + &internal::meta_info::resolve, + &func_type::arg, [](meta_handle handle, meta_any *any) { - return internal::invoke(handle, any, std::make_index_sequence::size>{}); + return internal::invoke(handle, any, std::make_index_sequence{}); }, []() -> meta_func { return &node; diff --git a/src/entt/meta/meta.hpp b/src/entt/meta/meta.hpp index f755cabfd..42ba1aac1 100644 --- a/src/entt/meta/meta.hpp +++ b/src/entt/meta/meta.hpp @@ -99,8 +99,8 @@ struct meta_data_node { const bool is_const; const bool is_static; meta_type_node *(* const type)(); - bool(* const set)(meta_handle, meta_any &); - meta_any(* const get)(meta_handle); + bool(* const set)(meta_handle, meta_any, meta_any); + meta_any(* const get)(meta_handle, meta_any); meta_data(* const meta)(); }; @@ -123,12 +123,14 @@ struct meta_func_node { struct meta_type_node { + using size_type = std::size_t; hashed_string name; meta_type_node * next; meta_prop_node * prop; const bool is_void; const bool is_integral; const bool is_floating_point; + const bool is_array; const bool is_enum; const bool is_union; const bool is_class; @@ -136,6 +138,7 @@ struct meta_type_node { const bool is_function; const bool is_member_object_pointer; const bool is_member_function_pointer; + const size_type extent; meta_type(* const remove_pointer)(); bool(* const destroy)(meta_handle); meta_type(* const meta)(); @@ -377,9 +380,9 @@ public: * @tparam Type Type of object to use to initialize the container. * @param type An instance of an object to use to initialize the container. */ - template, meta_any>>> + template>, meta_any>>> meta_any(Type &&type) { - using actual_type = std::decay_t; + using actual_type = std::remove_cv_t>; node = internal::meta_info::resolve(); compare_fn = &compare; @@ -656,7 +659,7 @@ public: * @tparam Type Type of object to use to initialize the handle. * @param obj A reference to an object to use to initialize the handle. */ - template, meta_handle>>> + template>, meta_handle>>> meta_handle(Type &&obj) ENTT_NOEXCEPT : meta_handle{0, std::forward(obj)} {} @@ -1230,22 +1233,56 @@ public: */ template inline bool set(meta_handle handle, Type &&value) const { - meta_any any{std::forward(value)}; - return node->set(handle, any); + return node->set(handle, meta_any{}, std::forward(value)); + } + + /** + * @brief Sets the i-th element of an array enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the setter results in an undefined + * behavior.
+ * The type of the value must coincide exactly with that of the array type + * enclosed by the meta data. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param handle An opaque pointer to an instance of the underlying type. + * @param index Position of the underlying element to set. + * @param value Parameter to use to set the underlying element. + * @return True in case of success, false otherwise. + */ + template + inline bool set(meta_handle handle, std::size_t index, Type &&value) const { + assert(index < node->type()->extent); + return node->set(handle, index, std::forward(value)); } /** * @brief Gets the value of the variable enclosed by a given meta type. * * It must be possible to cast the instance to the parent type of the meta - * function. Otherwise, invoking the getter results in an undefined - * behavior. + * data. Otherwise, invoking the getter results in an undefined behavior. * * @param handle An opaque pointer to an instance of the underlying type. * @return A meta any containing the value of the underlying variable. */ inline meta_any get(meta_handle handle) const ENTT_NOEXCEPT { - return node->get(handle); + return node->get(handle, meta_any{}); + } + + /** + * @brief Gets the i-th element of an array enclosed by a given meta type. + * + * It must be possible to cast the instance to the parent type of the meta + * data. Otherwise, invoking the getter results in an undefined behavior. + * + * @param handle An opaque pointer to an instance of the underlying type. + * @param index Position of the underlying element to get. + * @return A meta any containing the value of the underlying element. + */ + inline meta_any get(meta_handle handle, std::size_t index) const ENTT_NOEXCEPT { + assert(index < node->type()->extent); + return node->get(handle, index); } /** @@ -1497,6 +1534,9 @@ class meta_type { {} public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + /*! @brief Default constructor. */ inline meta_type() ENTT_NOEXCEPT : node{nullptr} @@ -1537,6 +1577,15 @@ public: return node->is_floating_point; } + /** + * @brief Indicates whether a given meta type refers to an array type or + * not. + * @return True if the underlying type is an array type, false otherwise. + */ + inline bool is_array() const ENTT_NOEXCEPT { + return node->is_array; + } + /** * @brief Indicates whether a given meta type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. @@ -1598,6 +1647,16 @@ public: return node->is_member_function_pointer; } + /** + * @brief If a given meta type refers to an array type, provides the number + * of elements of the array. + * @return The number of elements of the array if the underlying type is an + * array type, 0 otherwise. + */ + inline size_type extent() const ENTT_NOEXCEPT { + return node->extent; + } + /** * @brief Provides the meta type for which the pointer is defined. * @return The meta type for which the pointer is defined or this meta type @@ -2010,25 +2069,29 @@ struct meta_function_helper>: declt template inline bool destroy([[maybe_unused]] meta_handle handle) { - if constexpr(std::is_object_v) { - return handle.type() == meta_info::resolve()->meta() - ? (static_cast(handle.data())->~Type(), true) - : false; - } else { - return false; + bool accepted = false; + + if constexpr(std::is_object_v && !std::is_array_v) { + accepted = (handle.type() == meta_info::resolve()->meta()); + + if(accepted) { + static_cast(handle.data())->~Type(); + } } + + return accepted; } template inline meta_any construct(meta_any * const args, std::index_sequence) { - [[maybe_unused]] std::array can_cast{{(args+Indexes)->can_cast>()...}}; - [[maybe_unused]] std::array can_convert{{(std::get(can_cast) ? false : (args+Indexes)->can_convert>())...}}; + [[maybe_unused]] std::array can_cast{{(args+Indexes)->can_cast>>()...}}; + [[maybe_unused]] std::array can_convert{{(std::get(can_cast) ? false : (args+Indexes)->can_convert>>())...}}; meta_any any{}; if(((std::get(can_cast) || std::get(can_convert)) && ...)) { - ((std::get(can_convert) ? void((args+Indexes)->convert>()) : void()), ...); - any = Type{(args+Indexes)->cast>()...}; + ((std::get(can_convert) ? void((args+Indexes)->convert>>()) : void()), ...); + any = Type{(args+Indexes)->cast>>()...}; } return any; @@ -2036,55 +2099,88 @@ inline meta_any construct(meta_any * const args, std::index_sequence template -bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any &any) { +bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) { bool accepted = false; - if constexpr(Const) { - return accepted; - } else { + if constexpr(!Const) { if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { using helper_type = meta_function_helper>; using data_type = std::decay_t, typename helper_type::args_type>>; static_assert(std::is_invocable_v); - accepted = any.can_cast() || any.convert(); + accepted = value.can_cast() || value.convert(); auto *clazz = handle.try_cast(); if(accepted && clazz) { - std::invoke(Data, clazz, any.cast()); + std::invoke(Data, clazz, value.cast()); } } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::decay_t().*Data)>; + using data_type = std::remove_cv_t().*Data)>>; static_assert(std::is_invocable_v); - accepted = any.can_cast() || any.convert(); auto *clazz = handle.try_cast(); - if(accepted && clazz) { - std::invoke(Data, clazz) = any.cast(); + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + accepted = index.can_cast() && (value.can_cast() || value.convert()); + + if(accepted && clazz) { + std::invoke(Data, clazz)[index.cast()] = value.cast(); + } + } else { + accepted = value.can_cast() || value.convert(); + + if(accepted && clazz) { + std::invoke(Data, clazz) = value.cast(); + } } } else { static_assert(std::is_pointer_v); - using data_type = std::decay_t; - accepted = any.can_cast() || any.convert(); + using data_type = std::remove_cv_t>; - if(accepted) { - *Data = any.cast(); + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + accepted = index.can_cast() && (value.can_cast() || value.convert()); + + if(accepted) { + (*Data)[index.cast()] = value.cast(); + } + } else { + accepted = value.can_cast() || value.convert(); + + if(accepted) { + *Data = value.cast(); + } } } - - return accepted; } + + return accepted; } template -inline meta_any getter([[maybe_unused]] meta_handle handle) { - if constexpr(std::is_function_v> || std::is_member_pointer_v) { - static_assert(std::is_invocable_v); +inline meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) { + if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { + static_assert(std::is_invocable_v); auto *clazz = handle.try_cast(); return clazz ? std::invoke(Data, clazz) : meta_any{}; + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_cv_t().*Data)>>; + static_assert(std::is_invocable_v); + auto *clazz = handle.try_cast(); + + if constexpr(std::is_array_v) { + return (clazz && index.can_cast()) ? std::invoke(Data, clazz)[index.cast()] : meta_any{}; + } else { + return clazz ? std::invoke(Data, clazz) : meta_any{}; + } } else { static_assert(std::is_pointer_v); - return meta_any{*Data}; + + if constexpr(std::is_array_v>) { + return index.can_cast() ? (*Data)[index.cast()] : meta_any{}; + } else { + return *Data; + } } } @@ -2141,6 +2237,7 @@ meta_type_node * meta_node::resolve() ENTT_NOEXCEPT { std::is_void_v, std::is_integral_v, std::is_floating_point_v, + std::is_array_v, std::is_enum_v, std::is_union_v, std::is_class_v, @@ -2148,6 +2245,7 @@ meta_type_node * meta_node::resolve() ENTT_NOEXCEPT { std::is_function_v, std::is_member_object_pointer_v, std::is_member_function_pointer_v, + std::extent_v, []() -> meta_type { return internal::meta_info>::resolve(); }, diff --git a/test/entt/meta/meta.cpp b/test/entt/meta/meta.cpp index d07408801..0098ac39e 100644 --- a/test/entt/meta/meta.cpp +++ b/test/entt/meta/meta.cpp @@ -71,6 +71,11 @@ struct data_type { empty_type empty{}; }; +struct array_type { + static inline int global[3]; + int local[3]; +}; + struct func_type { int f(const base_type &, int a, int b) { return f(a, b); } int f(int a, int b) { value = a; return b*b; } @@ -154,6 +159,10 @@ struct Meta: public ::testing::Test { .data<&data_type::k>("k", std::make_pair(properties::prop_int, 3)) .data<&data_type::empty>("empty"); + entt::reflect("array") + .data<&array_type::global>("global") + .data<&array_type::local>("local"); + entt::reflect("func") .func(&func_type::f)>("f3") .func(&func_type::f)>("f2", std::make_pair(properties::prop_bool, false)) @@ -1010,6 +1019,65 @@ TEST_F(Meta, MetaDataSetterGetterMixed) { ASSERT_EQ(data.get(instance).cast(), 42); } +TEST_F(Meta, MetaDataArrayStatic) { + auto data = entt::resolve().data("global"); + + array_type::global[0] = 3; + array_type::global[1] = 5; + array_type::global[2] = 7; + + ASSERT_TRUE(data); + ASSERT_NE(data, entt::meta_data{}); + ASSERT_EQ(data.parent(), entt::resolve("array")); + ASSERT_EQ(data.type(), entt::resolve()); + ASSERT_STREQ(data.name(), "global"); + ASSERT_FALSE(data.is_const()); + ASSERT_TRUE(data.is_static()); + ASSERT_TRUE(data.type().is_array()); + ASSERT_EQ(data.type().extent(), 3); + ASSERT_EQ(data.get(nullptr, 0).cast(), 3); + ASSERT_EQ(data.get(nullptr, 1).cast(), 5); + ASSERT_EQ(data.get(nullptr, 2).cast(), 7); + ASSERT_FALSE(data.set(nullptr, 0, 'c')); + ASSERT_EQ(data.get(nullptr, 0).cast(), 3); + ASSERT_TRUE(data.set(nullptr, 0, data.get(nullptr, 0).cast()+2)); + ASSERT_TRUE(data.set(nullptr, 1, data.get(nullptr, 1).cast()+2)); + ASSERT_TRUE(data.set(nullptr, 2, data.get(nullptr, 2).cast()+2)); + ASSERT_EQ(data.get(nullptr, 0).cast(), 5); + ASSERT_EQ(data.get(nullptr, 1).cast(), 7); + ASSERT_EQ(data.get(nullptr, 2).cast(), 9); +} + +TEST_F(Meta, MetaDataArray) { + auto data = entt::resolve().data("local"); + array_type instance; + + instance.local[0] = 3; + instance.local[1] = 5; + instance.local[2] = 7; + + ASSERT_TRUE(data); + ASSERT_NE(data, entt::meta_data{}); + ASSERT_EQ(data.parent(), entt::resolve("array")); + ASSERT_EQ(data.type(), entt::resolve()); + ASSERT_STREQ(data.name(), "local"); + ASSERT_FALSE(data.is_const()); + ASSERT_FALSE(data.is_static()); + ASSERT_TRUE(data.type().is_array()); + ASSERT_EQ(data.type().extent(), 3); + ASSERT_EQ(data.get(instance, 0).cast(), 3); + ASSERT_EQ(data.get(instance, 1).cast(), 5); + ASSERT_EQ(data.get(instance, 2).cast(), 7); + ASSERT_FALSE(data.set(instance, 0, 'c')); + ASSERT_EQ(data.get(instance, 0).cast(), 3); + ASSERT_TRUE(data.set(instance, 0, data.get(instance, 0).cast()+2)); + ASSERT_TRUE(data.set(instance, 1, data.get(instance, 1).cast()+2)); + ASSERT_TRUE(data.set(instance, 2, data.get(instance, 2).cast()+2)); + ASSERT_EQ(data.get(instance, 0).cast(), 5); + ASSERT_EQ(data.get(instance, 1).cast(), 7); + ASSERT_EQ(data.get(instance, 2).cast(), 9); +} + TEST_F(Meta, MetaFunc) { auto func = entt::resolve().func("f2"); func_type instance{};