Class meta_any

Synopsis

#include <src/entt/meta/meta.hpp>

class meta_any

Description

Opaque wrapper for values of any type.

Mentioned in

Methods

meta_any overloadDefault constructor.
meta_any overloadConstructs a wrapper by directly initializing the new object.
meta_any overloadConstructs a wrapper from a given value.
meta_any overloadCopy constructor.
meta_any overloadMove constructor.
~meta_anyFrees the internal storage, whatever it means.
allow_cast overloadConverts an object in such a way that a given cast becomes viable.
as_associative_container overloadReturns an associative container proxy.
as_ref overloadAliasing constructor.
as_sequence_container overloadReturns a sequence container proxy.
cast overloadTries to cast an instance to a given type.
data overloadReturns an opaque pointer to the contained instance.
emplaceReplaces the contained object by creating a new instance directly.
get overloadGets the value of a given variable.
invoke overloadInvokes the underlying function, if possible.
operator boolReturns false if a wrapper is invalid, true otherwise.
operator*Indirection operator for dereferencing opaque objects.
operator= overloadCopy assignment operator.
operator= overloadMove assignment operator.
operator= overloadValue assignment operator.
operator==Checks if two wrappers differ in their content.
resetDestroys contained object.
setSets the value of a given variable.
try_cast overloadTries to cast an instance to a given type.
typeReturns the type of the underlying object.

Source

Lines 155-557 in src/entt/meta/meta.hpp.

class meta_any {
    enum class operation { DEREF, SEQ, ASSOC };

    using vtable_type = void(const operation, const any &, void *);

    template<typename Type>
    static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &from, [[maybe_unused]] void *to) {
        static_assert(std::is_same_v<std::remove_reference_t<std::remove_const_t<Type>>, Type>, "Invalid type");

        if constexpr(!std::is_void_v<Type>) {
            switch(op) {
            case operation::DEREF:
                if constexpr(is_meta_pointer_like_v<Type>) {
                    using element_type = std::remove_const_t<typename std::pointer_traits<Type>::element_type>;

                    if constexpr(std::is_function_v<element_type>) {
                        *static_cast<meta_any *>(to) = any_cast<Type>(from);
                    } else if constexpr(!std::is_same_v<std::remove_const_t<typename std::pointer_traits<Type>::element_type>, void>) {
                        using in_place_type = decltype(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(from)));
                        static_cast<meta_any *>(to)->emplace<in_place_type>(adl_meta_pointer_like<Type>::dereference(any_cast<const Type &>(from)));
                    }
                }
                break;
            case operation::SEQ:
                if constexpr(is_complete_v<meta_sequence_container_traits<Type>>) {
                    *static_cast<meta_sequence_container *>(to) = { std::in_place_type<Type>, std::move(const_cast<any &>(from)) };
                }
                break;
            case operation::ASSOC:
                if constexpr(is_complete_v<meta_associative_container_traits<Type>>) {
                    *static_cast<meta_associative_container *>(to) = { std::in_place_type<Type>, std::move(const_cast<any &>(from)) };
                }
                break;
            }
        }
    }

    void release() {
        if(node && node->dtor && storage.owner()) {
            node->dtor(storage.data());
        }
    }

    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<void>}
    {}

public:
    /*! @brief Default constructor. */
    meta_any() ENTT_NOEXCEPT
        : storage{},
          node{},
          vtable{&basic_vtable<void>}
    {}

    /**
     * @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<typename Type, typename... Args>
    explicit meta_any(std::in_place_type_t<Type>, Args &&... args)
        : storage{std::in_place_type<Type>, std::forward<Args>(args)...},
          node{internal::meta_info<Type>::resolve()},
          vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>}
    {}

    /**
     * @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<typename Type, typename = std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>>>
    meta_any(Type &&value)
        : storage{std::forward<Type>(value)},
          node{internal::meta_info<std::decay_t<Type>>::resolve()},
          vtable{&basic_vtable<std::decay_t<Type>>}
    {}

    /**
     * @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<void>)}
    {}

    /*! @brief Frees the internal storage, whatever it means. */
    ~meta_any() {
        release();
    }

    /**
     * @brief Copy assignment operator.
     * @param other The instance to copy from.
     * @return This meta any object.
     */
    meta_any & operator=(const meta_any &other) {
        release();
        vtable = other.vtable;
        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 {
        release();
        vtable = std::exchange(other.vtable, &basic_vtable<void>);
        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<typename Type>
    std::enable_if_t<!std::is_same_v<std::decay_t<Type>, meta_any>, meta_any &>
    operator=(Type &&value) {
        emplace<std::decay_t<Type>>(std::forward<Type>(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<typename... Args>
    meta_any invoke(const id_type id, Args &&... args) const;

    /*! @copydoc invoke */
    template<typename... Args>
    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<typename Type>
    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<typename Type>
    [[nodiscard]] const Type * try_cast() const {
        if(node) {
            if(const auto info = type_id<Type>(); node->info == info) {
                return any_cast<Type>(&storage);
            } else if(const auto *base = internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node); base) {
                return static_cast<const Type *>(base->cast(storage.data()));
            }
        }

        return nullptr;
    }

    /*! @copydoc try_cast */
    template<typename Type>
    [[nodiscard]] Type * try_cast() {
        if constexpr(std::is_const_v<Type>) {
            return std::as_const(*this).try_cast<Type>();
        } else {
            if(node) {
                if(const auto info = type_id<Type>(); node->info == info) {
                    return any_cast<Type>(&storage);
                } else if(const auto *base = internal::visit<&internal::meta_type_node::base>([info](const auto *curr) { return curr->type->info == info; }, node); base) {
                    return const_cast<Type *>(static_cast<const Type *>(base->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<typename Type>
    [[nodiscard]] Type cast() const {
        auto * const instance = try_cast<std::remove_reference_t<Type>>();
        ENTT_ASSERT(instance, "Invalid instance");
        return static_cast<Type>(*instance);
    }

    /*! @copydoc cast */
    template<typename Type>
    [[nodiscard]] Type cast() {
        // forces const on non-reference types to make them work also with wrappers for const references
        auto * const instance = try_cast<std::remove_reference_t<const Type>>();
        ENTT_ASSERT(instance, "Invalid instance");
        return static_cast<Type>(*instance);
    }

    /**
     * @brief Converts an object in such a way that a given cast becomes viable.
     * @param type Meta type to which the cast is requested.
     * @return A valid meta any object if there exists a viable conversion, an
     * invalid one otherwise.
     */
    [[nodiscard]] meta_any allow_cast(const meta_type &type) const;

    /**
     * @brief Converts an object in such a way that a given cast becomes viable.
     * @param type Meta type to which the cast is requested.
     * @return True if there exists a viable conversion, false otherwise.
     */
    [[nodiscard]] bool allow_cast(const meta_type &type);

    /**
     * @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<typename Type>
    [[nodiscard]] meta_any allow_cast() const {
        return allow_cast(internal::meta_info<Type>::resolve());
    }

    /**
     * @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<typename Type>
    bool allow_cast() {
        return allow_cast(internal::meta_info<Type>::resolve());
    }

    /**
     * @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<typename Type, typename... Args>
    void emplace(Args &&... args) {
        release();
        vtable = &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>;
        storage.emplace<Type>(std::forward<Args>(args)...);
        node = internal::meta_info<Type>::resolve();
    }

    /*! @brief Destroys contained object */
    void reset() {
        release();
        vtable = &basic_vtable<void>;
        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;
};





Add Discussion

Log in to comment