#ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP #include #include #include #include #include #include #include "../config/config.h" #include "../core/any.hpp" #include "../core/fwd.hpp" #include "../core/utility.hpp" #include "../core/type_info.hpp" #include "../core/type_traits.hpp" #include "adl_pointer.hpp" #include "ctx.hpp" #include "node.hpp" #include "range.hpp" #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; /*! @brief Proxy object for sequence containers. */ class meta_sequence_container { template struct meta_sequence_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_sequence_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for sequence containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : value_type_fn{&meta_sequence_container_proxy::value_type}, size_fn{&meta_sequence_container_proxy::size}, resize_fn{&meta_sequence_container_proxy::resize}, clear_fn{&meta_sequence_container_proxy::clear}, begin_fn{&meta_sequence_container_proxy::begin}, end_fn{&meta_sequence_container_proxy::end}, insert_fn{&meta_sequence_container_proxy::insert}, erase_fn{&meta_sequence_container_proxy::erase}, get_fn{&meta_sequence_container_proxy::get}, storage{std::move(instance)} {} [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool resize(size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline std::pair insert(iterator, meta_any); inline std::pair erase(iterator); [[nodiscard]] inline meta_any operator[](size_type); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; bool(* resize_fn)(any &, size_type) = nullptr; bool(* clear_fn)(any &) = nullptr; iterator(* begin_fn)(any &) = nullptr; iterator(* end_fn)(any &) = nullptr; std::pair(* insert_fn)(any &, iterator, meta_any &) = nullptr; std::pair(* erase_fn)(any &, iterator) = nullptr; meta_any(* get_fn)(any &, size_type) = nullptr; any storage{}; }; /*! @brief Proxy object for associative containers. */ class meta_associative_container { template struct meta_associative_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_associative_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for associative containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : key_only_container{is_key_only_meta_associative_container_v}, key_type_fn{&meta_associative_container_proxy::key_type}, mapped_type_fn{&meta_associative_container_proxy::mapped_type}, value_type_fn{&meta_associative_container_proxy::value_type}, size_fn{&meta_associative_container_proxy::size}, clear_fn{&meta_associative_container_proxy::clear}, begin_fn{&meta_associative_container_proxy::begin}, end_fn{&meta_associative_container_proxy::end}, insert_fn{&meta_associative_container_proxy::insert}, erase_fn{&meta_associative_container_proxy::erase}, find_fn{&meta_associative_container_proxy::find}, storage{std::move(instance)} {} [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline bool insert(meta_any, meta_any); inline bool erase(meta_any); [[nodiscard]] inline iterator find(meta_any); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: bool key_only_container{}; meta_type(* key_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(* mapped_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(* value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(* size_fn)(const any &) ENTT_NOEXCEPT = nullptr; bool(* clear_fn)(any &) = nullptr; iterator(* begin_fn)(any &) = nullptr; iterator(* end_fn)(any &) = nullptr; bool(* insert_fn)(any &, meta_any &, meta_any &) = nullptr; bool(* erase_fn)(any &, meta_any &) = nullptr; iterator(* find_fn)(any &, meta_any &) = nullptr; any storage{}; }; /*! @brief Opaque wrapper for values of any type. */ class meta_any { enum class operation { DTOR, DEREF, SEQ, ASSOC }; using vtable_type = void(const operation, const any &, void *); template static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] void *to) { static_assert(std::is_same_v>, Type>, "Invalid type"); if constexpr(!std::is_void_v) { switch(op) { case operation::DTOR: if(auto *curr = static_cast(to); curr->dtor && from.owner()) { curr->dtor(const_cast(from).data()); } break; case operation::DEREF: if constexpr(is_meta_pointer_like_v) { using element_type = std::remove_const_t::element_type>; if constexpr(std::is_function_v) { *static_cast(to) = any_cast(from); } else if constexpr(!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(from))); static_cast(to)->emplace(adl_meta_pointer_like::dereference(any_cast(from))); } } break; case operation::SEQ: if constexpr(is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; case operation::ASSOC: if constexpr(is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; } } } meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT : storage{std::move(ref)}, node{storage ? other.node : nullptr}, vtable{storage ? other.vtable : &basic_vtable} {} public: /*! @brief Default constructor. */ meta_any() ENTT_NOEXCEPT : storage{}, node{}, vtable{&basic_vtable} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit meta_any(std::in_place_type_t, Args &&... args) : storage{std::in_place_type, std::forward(args)...}, node{internal::meta_info::resolve()}, vtable{&basic_vtable>>} {} /** * @brief Constructs a wrapper that holds an unmanaged object. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template meta_any(std::reference_wrapper value) : meta_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, meta_any>>> meta_any(Type &&value) : storage{std::forward(value)}, node{internal::meta_info>::resolve()}, vtable{&basic_vtable>} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ meta_any(const meta_any &other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ meta_any(meta_any &&other) ENTT_NOEXCEPT : storage{std::move(other.storage)}, node{std::exchange(other.node, nullptr)}, vtable{std::exchange(other.vtable, &basic_vtable)} {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { vtable(operation::DTOR, storage, node); } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This meta any object. */ meta_any & operator=(const meta_any &other) { std::exchange(vtable, other.vtable)(operation::DTOR, storage, node); storage = other.storage; node = other.node; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This meta any object. */ meta_any & operator=(meta_any &&other) ENTT_NOEXCEPT { std::exchange(vtable, std::exchange(other.vtable, &basic_vtable))(operation::DTOR, storage, node); storage = std::move(other.storage); node = std::exchange(other.node, nullptr); return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This meta any object. */ template [[deprecated("Use std::in_place_type, entt::make_meta, emplace or forward_as_meta instead")]] meta_any & operator=(std::reference_wrapper value) { emplace(value.get()); return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This meta any object. */ template std::enable_if_t, meta_any>, meta_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /** * @brief Returns the type of the underlying object. * @return The type of the underlying object, if any. */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void * data() const ENTT_NOEXCEPT { return storage.data(); } /*! @copydoc data */ [[nodiscard]] void * data() ENTT_NOEXCEPT { return storage.data(); } /** * @brief Invokes the underlying function, if possible. * * @sa meta_func::invoke * * @tparam Args Types of arguments to use to invoke the function. * @param id Unique identifier. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, Args &&... args) const; /*! @copydoc invoke */ template meta_any invoke(const id_type id, Args &&... args); /** * @brief Sets the value of a given variable. * * The type of the value must be such that a cast or conversion to the type * of the variable is possible. Otherwise, invoking the setter does nothing. * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, Type &&value); /** * @brief Gets the value of a given variable. * @param id Unique identifier. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id) const; /*! @copydoc get */ [[nodiscard]] meta_any get(const id_type id); /** * @brief Tries to cast an instance to a given type. * @tparam Type Type to which to cast the instance. * @return A (possibly null) pointer to the contained instance. */ template [[nodiscard]] const Type * try_cast() const { if(node) { if(const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { return static_cast(base->cast(storage.data())); } } return nullptr; } /*! @copydoc try_cast */ template [[nodiscard]] Type * try_cast() { if(node) { if(const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if(const auto *base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type()->info == info; }, node); base) { return static_cast(const_cast *>(base->cast(static_cast &>(storage).data()))); } } return nullptr; } /** * @brief Tries to cast an instance to a given type. * * The type of the instance must be such that the cast is possible. * * @warning * Attempting to perform an invalid cast results in undefined behavior. * * @tparam Type Type to which to cast the instance. * @return A reference to the contained instance. */ template [[nodiscard]] Type cast() const { auto * const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc cast */ template [[nodiscard]] Type cast() { // forces const on non-reference types to make them work also with wrappers for const references auto * const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return A valid meta any object if there exists a viable conversion, an * invalid one otherwise. */ template [[nodiscard]] meta_any allow_cast() const { if(try_cast>() != nullptr) { return as_ref(); } else if(node) { if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { return conv->conv(storage.data()); } } return {}; } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return True if there exists a viable conversion, false otherwise. */ template bool allow_cast() { // forces const on non-reference types to make them work also with wrappers for const references if(try_cast>() != nullptr) { return true; } else if(node) { if(const auto * const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto *curr) { return curr->type()->info == info; }, node); conv) { *this = conv->conv(std::as_const(storage).data()); return true; } } return false; } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, storage, node); storage.emplace(std::forward(args)...); node = internal::meta_info::resolve(); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); storage.reset(); node = nullptr; } /** * @brief Returns a sequence container proxy. * @return A sequence container proxy for the underlying object. */ [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { meta_sequence_container proxy; vtable(operation::SEQ, storage.as_ref(), &proxy); return proxy; } /*! @copydoc as_sequence_container */ [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { meta_sequence_container proxy; vtable(operation::SEQ, storage.as_ref(), &proxy); return proxy; } /** * @brief Returns an associative container proxy. * @return An associative container proxy for the underlying object. */ [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT { meta_associative_container proxy; vtable(operation::ASSOC, storage.as_ref(), &proxy); return proxy; } /*! @copydoc as_associative_container */ [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { meta_associative_container proxy; vtable(operation::ASSOC, storage.as_ref(), &proxy); return proxy; } /** * @brief Indirection operator for dereferencing opaque objects. * @return A wrapper that shares a reference to an unmanaged object if the * wrapped element is dereferenceable, an invalid meta any otherwise. */ [[nodiscard]] meta_any operator*() const ENTT_NOEXCEPT { meta_any ret{}; vtable(operation::DEREF, storage, &ret); return ret; } /** * @brief Returns false if a wrapper is invalid, true otherwise. * @return False if the wrapper is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const meta_any &other) const { return (!node && !other.node) || (node && other.node && node->info == other.node->info && storage == other.storage); } /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { return meta_any{*this, storage.as_ref()}; } /*! @copydoc as_ref */ [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { return meta_any{*this, storage.as_ref()}; } private: any storage; internal::meta_type_node *node; vtable_type *vtable; }; /** * @brief Checks if two wrappers differ in their content. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ [[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. * @return A properly initialized wrapper for an object of the given type. */ template meta_any make_meta(Args &&... args) { return meta_any{std::in_place_type, std::forward(args)...}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template meta_any forward_as_meta(Type &&value) { return meta_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } /** * @brief Opaque pointers to instances of any type. * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
* Handles are used to generate references to actual objects when needed. */ struct meta_handle { /*! @brief Default constructor. */ meta_handle() = default; /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle &) = delete; /*! @brief Default move constructor. */ meta_handle(meta_handle &&) = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This meta handle. */ meta_handle & operator=(const meta_handle &) = delete; /** * @brief Default move assignment operator. * @return This meta handle. */ meta_handle & operator=(meta_handle &&) = default; /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(Type &value) ENTT_NOEXCEPT : meta_handle{} { if constexpr(std::is_same_v, meta_any>) { any = value.as_ref(); } else { any.emplace(value); } } /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(any); } /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any * operator->() { return &any; } /*! @copydoc operator-> */ [[nodiscard]] const meta_any * operator->() const { return &any; } private: meta_any any; }; /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Node type. */ using node_type = internal::meta_prop_node; /** * @brief Constructs an instance from a given node. * @param curr The underlying node with which to construct the instance. */ meta_prop(const node_type *curr = nullptr) ENTT_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; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type *node; }; /*! @brief Opaque wrapper for constructors. */ struct meta_ctor { /*! @brief Node type. */ using node_type = internal::meta_ctor_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_ctor(const node_type *curr = nullptr) ENTT_NOEXCEPT : node{curr} {} /** * @brief Returns the type to which an object belongs. * @return The type to which the object belongs. */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a constructor. * @return The number of arguments accepted by the constructor. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Returns the type of the i-th argument of a constructor. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a constructor. */ [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any invoke(meta_any * const args, const size_type sz) const { return sz == arity() ? node->invoke(args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; return invoke(arguments, sizeof...(Args)); } /** * @brief Returns a range to use to visit all properties. * @return An iterable range to use to visit all properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type *node; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Node type. */ using node_type = internal::meta_data_node; /*! @copydoc meta_prop::meta_prop */ meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT : node{curr} {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /*! @copydoc meta_any::type */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* The type of the value must be such that a cast or conversion to the type * of the variable is possible. Otherwise, invoking the setter does nothing. * * @tparam Type Type of value to assign. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(meta_handle instance, Type &&value) const { return node->set && node->set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { return node->get(std::move(instance)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type *node; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Node type. */ using node_type = internal::meta_func_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT : node{curr} {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ [[nodiscard]] inline meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Invokes the underlying function, if possible. * * To invoke a member function, the parameters must be such that a cast or * conversion to the required types is possible. Otherwise, an empty and * thus invalid wrapper is returned.
* It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any * const args, const size_type sz) const { return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; return invoke(std::move(instance), arguments, sizeof...(Args)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type *node; }; /*! @brief Opaque wrapper for types. */ class meta_type { static bool can_cast_or_convert(const internal::meta_type_node *type, const type_info info) ENTT_NOEXCEPT { if(type->info == info) { return true; } for(const auto *curr = type->conv; curr; curr = curr->next) { if(curr->type()->info == info) { return true; } } for(const auto *curr = type->base; curr; curr = curr->next) { if(auto *target = curr->type(); can_cast_or_convert(target, info)) { return true; } } return false; } template [[nodiscard]] static const internal::meta_ctor_node * ctor(const internal::meta_ctor_node *curr, std::index_sequence) { for(; curr; curr = curr->next) { if(curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { return curr; } } return nullptr; } template void unregister_all(Node **curr) { while(*curr) { (unregister_all(&((*curr)->*Member)), ...); *curr = std::exchange((*curr)->next, nullptr); } } public: /*! @brief Node type. */ using node_type = internal::meta_type_node; /*! @brief Node type. */ using base_node_type = internal::meta_base_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_type(node_type *curr = nullptr) ENTT_NOEXCEPT : node{curr} {} /** * @brief Constructs an instance from a given base node. * @param curr The base node with which to construct the instance. */ meta_type(base_node_type *curr) ENTT_NOEXCEPT : node{curr ? curr->type() : nullptr} {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ [[nodiscard]] type_info info() const ENTT_NOEXCEPT { return node->info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { return node->size_of; } /** * @brief Checks whether a type refers to void or not. * @return True if the underlying type is void, false otherwise. */ [[nodiscard]] bool is_void() const ENTT_NOEXCEPT { return node->is_void; } /** * @brief Checks whether a type refers to an integral type or not. * @return True if the underlying type is an integral type, false otherwise. */ [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT { return node->is_integral; } /** * @brief Checks whether a type refers to a floating-point type or not. * @return True if the underlying type is a floating-point type, false * otherwise. */ [[nodiscard]] bool is_floating_point() const ENTT_NOEXCEPT { return node->is_floating_point; } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { return node->is_array; } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { return node->is_enum; } /** * @brief Checks whether a type refers to an union or not. * @return True if the underlying type is an union, false otherwise. */ [[nodiscard]] bool is_union() const ENTT_NOEXCEPT { return node->is_union; } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { return node->is_class; } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { return node->is_pointer; } /** * @brief Checks whether a type refers to a function pointer or not. * @return True if the underlying type is a function pointer, false * otherwise. */ [[nodiscard]] bool is_function_pointer() const ENTT_NOEXCEPT { return node->is_function_pointer; } /** * @brief Checks whether a type refers to a pointer to data member or not. * @return True if the underlying type is a pointer to data member, false * otherwise. */ [[nodiscard]] bool is_member_object_pointer() const ENTT_NOEXCEPT { return node->is_member_object_pointer; } /** * @brief Checks whether a type refers to a pointer to member function or * not. * @return True if the underlying type is a pointer to member function, * false otherwise. */ [[nodiscard]] bool is_member_function_pointer() const ENTT_NOEXCEPT { return node->is_member_function_pointer; } /** * @brief Checks whether a type is a pointer-like type or not. * @return True if the underlying type is a pointer-like one, false * otherwise. */ [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { return node->is_pointer_like; } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { return node->is_sequence_container; } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { return node->is_associative_container; } /** * @brief Checks whether a type refers to a recognized class template * specialization or not. * @return True if the type is a recognized class template specialization, * false otherwise. */ [[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { return node->template_info.is_template_specialization; } /** * @brief Returns the number of template arguments, if any. * @return The number of template arguments, if any. */ [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { return node->template_info.arity; } /** * @brief Returns a tag for the class template of the underlying type. * * @sa meta_class_template_tag * * @return The tag for the class template of the underlying type. */ [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { return is_template_specialization() ? node->template_info.type() : meta_type{}; } /** * @brief Returns the type of the i-th template argument of a type. * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ [[nodiscard]] inline meta_type template_arg(size_type index) const ENTT_NOEXCEPT { return index < template_arity() ? node->template_info.arg(index) : meta_type{}; } /** * @brief Provides the number of dimensions of an array type. * @return The number of dimensions in case of array types, 0 otherwise. */ [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { return node->rank; } /** * @brief The number of elements along the given dimension of an array type. * @param dim The dimension of which to return the number of elements. * @return The number of elements along the given dimension in case of array * types, 0 otherwise. */ [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { return node->extent(dim); } /** * @brief Provides the type for which the pointer is defined. * @return The type for which the pointer is defined or this type if it * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { return node->remove_pointer(); } /** * @brief Provides the type for which the array is defined. * @return The type for which the array is defined or this type if it * doesn't refer to an array type. */ [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { return node->remove_extent(); } /** * @brief Returns a range to use to visit top-level base meta types. * @return An iterable range to use to visit top-level base meta types. */ [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { return node->base; } /** * @brief Returns the base meta type associated with a given identifier. * @param id Unique identifier. * @return The base meta type associated with the given identifier, if any. */ [[nodiscard]] meta_type base(const id_type id) const { return internal::meta_visit<&node_type::base>([id](const auto *curr) { return curr->type()->id == id; }, node); } /** * @brief Returns a range to use to visit top-level constructors. * @return An iterable range to use to visit top-level constructors. */ [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { return node->ctor; } /** * @brief Returns a constructor for a given list of types of arguments. * @tparam Args Constructor arguments. * @return The requested constructor, if any. */ template [[nodiscard]] meta_ctor ctor() const { return ctor(node->ctor, std::make_index_sequence{}); } /** * @brief Returns a range to use to visit top-level data. * @return An iterable range to use to visit top-level data. */ [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { return node->data; } /** * @brief Returns the data associated with a given identifier. * * The data of the base classes will also be visited, if any. * * @param id Unique identifier. * @return The data associated with the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { return internal::meta_visit<&node_type::data>([id](const auto *curr) { return curr->id == id; }, node); } /** * @brief Returns a range to use to visit top-level functions. * @return An iterable range to use to visit top-level functions. */ [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { return node->func; } /** * @brief Returns the function associated with a given identifier. * * The functions of the base classes will also be visited, if any.
* In the case of overloaded functions, the first one with the required * identifier will be returned. * * @param id Unique identifier. * @return The function associated with the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { return internal::meta_visit<&node_type::func>([id](const auto *curr) { return curr->id == id; }, node); } /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any * const args, const size_type sz) const { meta_any ret{}; internal::meta_visit<&node_type::ctor>([args, sz, &ret](const auto *curr) { return (curr->arity == sz) && (ret = curr->invoke(args)); }, node); return ret; } /** * @copybrief construct * * @sa construct * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; return construct(arguments, sizeof...(Args)); } /** * @brief Invokes a function given an identifier, if possible. * * It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @sa meta_func::invoke * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any * const args, const size_type sz) const { const internal::meta_func_node* candidate{}; size_type extent{sz + 1u}; bool ambiguous{}; for(auto *it = internal::meta_visit<&node_type::func>([id, sz](const auto *curr) { return curr->id == id && curr->arity == sz; }, node); it && it->id == id && it->arity == sz; it = it->next) { size_type direct{}; size_type ext{}; for(size_type next{}; next < sz && next == (direct + ext); ++next) { const auto type = args[next].type(); const auto req = it->arg(next).info(); type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); } if((direct + ext) == sz) { if(ext < extent) { candidate = it; extent = ext; ambiguous = false; } else if(ext == extent) { ambiguous = true; } } } return (candidate && !ambiguous) ? candidate->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; return invoke(id, std::move(instance), arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* The type of the value must be such that a cast or conversion to the type * of the variable is possible. Otherwise, invoking the setter does nothing. * * @tparam Type Type of value to assign. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, meta_handle instance, Type &&value) const { const auto candidate = data(id); return candidate && candidate.set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); return candidate ? candidate.get(std::move(instance)) : meta_any{}; } /** * @brief Returns a range to use to visit top-level properties. * @return An iterable range to use to visit top-level properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * * Properties of the base classes will also be visited, if any. * * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto *curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } /** * @brief Checks if two objects refer to the same type. * @param other The object with which to compare. * @return True if the objects refer to the same type, false otherwise. */ [[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT { return (!node && !other.node) || (node && other.node && node->info == other.node->info); } /** * @brief Resets a type and all its parts. * * This function resets a type and all its data members, member functions * and properties, as well as its constructors, destructors and conversion * functions if any.
* Base classes aren't reset but the link between the two types is removed. * * The type is also removed from the list of searchable types. */ void reset() ENTT_NOEXCEPT { for(auto** it = internal::meta_context::global(); *it; it = &(*it)->next) { if(*it == node) { *it = (*it)->next; break; } } unregister_all(&node->prop); unregister_all(&node->base); unregister_all(&node->conv); unregister_all<&internal::meta_ctor_node::prop>(&node->ctor); unregister_all<&internal::meta_data_node::prop>(&node->data); unregister_all<&internal::meta_func_node::prop>(&node->func); node->id = {}; node->ctor = node->def_ctor; node->dtor = nullptr; } private: node_type *node; }; /** * @brief Checks if two objects refer to the same type. * @param lhs An object, either valid or not. * @param rhs An object, either valid or not. * @return False if the objects refer to the same node, true otherwise. */ [[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } [[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { return node; } template meta_any meta_any::invoke(const id_type id, Args &&... args) const { return type().invoke(id, *this, std::forward(args)...); } template meta_any meta_any::invoke(const id_type id, Args &&... args) { return type().invoke(id, *this, std::forward(args)...); } template bool meta_any::set(const id_type id, Type &&value) { return type().set(id, *this, std::forward(value)); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) const { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) { return type().get(id, *this); } [[nodiscard]] inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } [[nodiscard]] inline meta_type meta_data::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { return node->type(); } [[nodiscard]] inline meta_type meta_func::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { return node->ret(); } [[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } /*! @brief Opaque iterator for sequence containers. */ class meta_sequence_container::meta_iterator { /*! @brief A sequence container can access the underlying iterator. */ friend class meta_sequence_container; enum class operation { INCR, DEREF }; using vtable_type = void(const operation, const any &, void *); template static void basic_vtable(const operation op, const any &from, void *to) { switch(op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: static_cast(to)->emplace::reference>(*any_cast(from)); break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = meta_any; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs a meta iterator from a given iterator. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(It iter) : vtable{&basic_vtable}, handle{std::move(iter)} {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator & operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), *this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { meta_any other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type *vtable{}; any handle{}; }; template struct meta_sequence_container::meta_sequence_container_proxy { using traits_type = meta_sequence_container_traits; [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool resize(any &container, size_type sz) { auto * const cont = any_cast(&container); return cont && traits_type::resize(*cont, sz); } [[nodiscard]] static bool clear(any &container) { auto * const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any &container) { if(auto * const cont = any_cast(&container); cont) { return iterator{traits_type::begin(*cont)}; } return iterator{traits_type::cbegin(any_cast(container))}; } [[nodiscard]] static iterator end(any &container) { if(auto * const cont = any_cast(&container); cont) { return iterator{traits_type::end(*cont)}; } return iterator{traits_type::cend(any_cast(container))}; } [[nodiscard]] static std::pair insert(any &container, iterator it, meta_any &value) { if(auto * const cont = any_cast(&container); cont) { // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector if(value.allow_cast() || value.allow_cast()) { const auto *element = value.try_cast>(); auto ret = traits_type::insert(*cont, any_cast(it.handle), element ? *element : value.cast()); return { iterator{std::move(ret.first)}, ret.second }; } } return {}; } [[nodiscard]] static std::pair erase(any &container, iterator it) { if(auto * const cont = any_cast(&container); cont) { auto ret = traits_type::erase(*cont, any_cast(it.handle)); return { iterator{std::move(ret.first)}, ret.second }; } return {}; } [[nodiscard]] static meta_any get(any &container, size_type pos) { if(auto * const cont = any_cast(&container); cont) { return meta_any{std::in_place_type, traits_type::get(*cont, pos)}; } return meta_any{std::in_place_type, traits_type::cget(any_cast(container), pos)}; } }; /** * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ [[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /** * @brief Returns the size of a container. * @return The size of the container. */ [[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /** * @brief Resizes a container to contain a given number of elements. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::resize(size_type sz) { return resize_fn(storage, sz); } /** * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { return clear_fn(storage); } /** * @brief Returns an iterator to the first element of a container. * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { return begin_fn(storage); } /** * @brief Returns an iterator that is past the last element of a container. * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { return end_fn(storage); } /** * @brief Inserts an element at a specified location of a container. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A pair consisting of an iterator to the inserted element (in case of * success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { return insert_fn(storage, it, value); } /** * @brief Removes a given element from a container. * @param it Iterator to the element to remove. * @return A pair consisting of an iterator following the last removed element * (in case of success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::erase(iterator it) { return erase_fn(storage, it); } /** * @brief Returns a reference to the element at a given location of a container * (no bounds checking is performed). * @param pos The position of the element to return. * @return A reference to the requested element properly wrapped. */ [[nodiscard]] inline meta_any meta_sequence_container::operator[](size_type pos) { return get_fn(storage, pos); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } /*! @brief Opaque iterator for associative containers. */ class meta_associative_container::meta_iterator { enum operation { INCR, DEREF }; using vtable_type = void(const operation, const any &, void *); template static void basic_vtable(const operation op, const any &from, void *to) { switch(op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: const auto &it = any_cast(from); if constexpr(KeyOnly) { static_cast *>(to)->first.emplace(*it); } else { static_cast *>(to)->first.emplacefirst))>(it->first); static_cast *>(to)->second.emplacesecond))>(it->second); } break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = std::pair; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs an meta iterator from a given iterator. * @tparam KeyOnly True if the container is also key-only, false otherwise. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(std::integral_constant, It iter) : vtable{&basic_vtable}, handle{std::move(iter)} {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator & operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), *this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { reference other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type *vtable{}; any handle{}; }; template struct meta_associative_container::meta_associative_container_proxy { using traits_type = meta_associative_container_traits; [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { if constexpr(is_key_only_meta_associative_container_v) { return meta_type{}; } else { return internal::meta_info::resolve(); } } [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any &container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool clear(any &container) { auto * const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any &container) { if(auto * const cont = any_cast(&container); cont) { return iterator{is_key_only_meta_associative_container{}, traits_type::begin(*cont)}; } return iterator{is_key_only_meta_associative_container{}, traits_type::cbegin(any_cast(container))}; } [[nodiscard]] static iterator end(any &container) { if(auto * const cont = any_cast(&container); cont) { return iterator{is_key_only_meta_associative_container{}, traits_type::end(*cont)}; } return iterator{is_key_only_meta_associative_container{}, traits_type::cend(any_cast(container))}; } [[nodiscard]] static bool insert(any &container, meta_any &key, meta_any &value) { if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { if constexpr(is_key_only_meta_associative_container_v) { return traits_type::insert(*cont, key.cast()); } else { return value.allow_cast() && traits_type::insert(*cont, key.cast(), value.cast()); } } return false; } [[nodiscard]] static bool erase(any &container, meta_any &key) { if(auto * const cont = any_cast(&container); cont && key.allow_cast()) { return traits_type::erase(*cont, key.cast()); } return false; } [[nodiscard]] static iterator find(any &container, meta_any &key) { if(key.allow_cast()) { if(auto * const cont = any_cast(&container); cont) { return iterator{is_key_only_meta_associative_container{}, traits_type::find(*cont, key.cast())}; } return iterator{is_key_only_meta_associative_container{}, traits_type::cfind(any_cast(container), key.cast())}; } return {}; } }; /** * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ [[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { return key_only_container; } /** * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { return key_type_fn(); } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { return mapped_type_fn(); } /*! @copydoc meta_sequence_container::value_type */ [[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { return begin_fn(storage); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { return end_fn(storage); } /** * @brief Inserts an element (a key/value pair) into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { return insert_fn(storage, key, value); } /** * @brief Removes the specified element from a container. * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ inline bool meta_associative_container::erase(meta_any key) { return erase_fn(storage, key); } /** * @brief Returns an iterator to the element with a given key, if any. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { return find_fn(storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } } #endif