Class meta_any

Synopsis

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

class meta_any

Description

Opaque wrapper for values of any type.

This class uses a technique called small buffer optimization (SBO) to get rid of memory allocations if possible. This should improve overall performance.

Mentioned in

Methods

meta_any OverloadDefault constructor.
meta_any OverloadConstructs a meta any by directly initializing the new object.
meta_any OverloadConstructs a meta any that holds an unmanaged object.
meta_any OverloadConstructs a meta any from a given value.
meta_any OverloadCopy constructor.
meta_any OverloadMove constructor.
~meta_anyFrees the internal storage, whatever it means.
as_associative_containerReturns an associative container proxy.
as_sequence_containerReturns a sequence container proxy.
cast OverloadTries to cast an instance to a given type.
convert OverloadTries to convert an instance to a given type and returns it.
convert OverloadTries to convert 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.
getGets the value of a given variable.
invokeInvokes the underlying function, if possible.
operator boolReturns false if a wrapper is empty, true otherwise.
operator*Indirection operator for dereferencing opaque objects.
operator=Assignment operator.
operator==Checks if two wrappers differ in their content.
refAliasing constructor.
setSets the value of a given variable.
try_cast OverloadTries to cast an instance to a given type.
typeReturns the meta type of the underlying object.

Source

Lines 165-514 in src/entt/meta/meta.hpp.

class meta_any {
    using dereference_operator_type = meta_any(meta_any &);

    template<typename Type>
    [[nodiscard]] static meta_any dereference_operator(meta_any &any) {
        if constexpr(is_meta_pointer_like_v<Type>) {
            using pointed_type = std::remove_reference_t<decltype(*std::declval<Type>())>;

            if constexpr(std::is_const_v<pointed_type> && std::is_copy_constructible_v<pointed_type>) {
                auto ptr = any.cast<Type>();
                return ptr ? std::as_const(*ptr) : meta_any{};
            } else if constexpr(!std::is_const_v<pointed_type>) {
                auto ptr = any.cast<Type>();
                return ptr ? std::ref(*ptr) : meta_any{};
            } else {
                return {};
            }
        } else {
            return {};
        }
    }

    template<typename Type>
    [[nodiscard]] static meta_sequence_container meta_sequence_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT {
        if constexpr(has_meta_sequence_container_traits_v<Type>) {
            return static_cast<Type *>(container);
        } else {
            return {};
        }
    }

    template<typename Type>
    [[nodiscard]] static meta_associative_container meta_associative_container_factory([[maybe_unused]] void *container) ENTT_NOEXCEPT {
        if constexpr(has_meta_associative_container_traits_v<Type>) {
            return static_cast<Type *>(container);
        } else {
            return {};
        }
    }

public:
    /*! @brief Default constructor. */
    meta_any() ENTT_NOEXCEPT
        : storage{},
          node{},
          deref{nullptr},
          seq_factory{nullptr},
          assoc_factory{nullptr}
    {}

    /**
     * @brief Constructs a meta any 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>, [[maybe_unused]] Args &&... args)
        : storage(std::in_place_type<Type>, std::forward<Args>(args)...),
          node{internal::meta_info<Type>::resolve()},
          deref{&dereference_operator<Type>},
          seq_factory{&meta_sequence_container_factory<Type>},
          assoc_factory{&meta_associative_container_factory<Type>}
    {}

    /**
     * @brief Constructs a meta any 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<typename Type>
    meta_any(std::reference_wrapper<Type> value)
        : storage{value},
          node{internal::meta_info<Type>::resolve()},
          deref{&dereference_operator<Type>},
          seq_factory{&meta_sequence_container_factory<Type>},
          assoc_factory{&meta_associative_container_factory<Type>}
    {}

    /**
     * @brief Constructs a meta any 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::remove_cv_t<std::remove_reference_t<Type>>, meta_any>>>
    meta_any(Type &&value)
        : meta_any{std::in_place_type<std::remove_cv_t<std::remove_reference_t<Type>>>, std::forward<Type>(value)}
    {}

    /**
     * @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)
        : meta_any{}
    {
        swap(*this, other);
    }

    /*! @brief Frees the internal storage, whatever it means. */
    ~meta_any() {
        if(node && node->dtor) {
            node->dtor(storage.data());
        }
    }

    /**
     * @brief Assignment operator.
     * @param other The instance to assign from.
     * @return This meta any object.
     */
    meta_any & operator=(meta_any other) {
        swap(other, *this);
        return *this;
    }

    /**
     * @brief Returns the meta type of the underlying object.
     * @return The meta 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 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 meta any containing the returned value, if any.
     */
    template<typename... Args>
    meta_any invoke(const id_type id, Args &&... args) const;

    /**
     * @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) const;

    /**
     * @brief Gets the value of a given variable.
     * @param id Unique identifier.
     * @return A meta any containing the value of the underlying variable.
     */
    [[nodiscard]] meta_any get(const id_type id) const;

    /**
     * @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 = internal::meta_info<Type>::resolve()->info; node->info == info) {
                return static_cast<const Type *>(storage.data());
            } else if(const auto *base = internal::find_if<&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() {
        return const_cast<Type *>(std::as_const(*this).try_cast<Type>());
    }

    /**
     * @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 a cast that isn't viable results in undefined
     * behavior.
     *
     * @tparam Type Type to which to cast the instance.
     * @return A reference to the contained instance.
     */
    template<typename Type>
    [[nodiscard]] const Type & cast() const {
        auto * const actual = try_cast<Type>();
        ENTT_ASSERT(actual);
        return *actual;
    }

    /*! @copydoc cast */
    template<typename Type>
    [[nodiscard]] Type & cast() {
        return const_cast<Type &>(std::as_const(*this).cast<Type>());
    }

    /**
     * @brief Tries to convert an instance to a given type and returns it.
     * @tparam Type Type to which to convert the instance.
     * @return A valid meta any object if the conversion is possible, an invalid
     * one otherwise.
     */
    template<typename Type>
    [[nodiscard]] meta_any convert() const {
        if(node) {
            if(const auto info = internal::meta_info<Type>::resolve()->info; node->info == info) {
                return *this;
            } else if(const auto * const conv = internal::find_if<&internal::meta_type_node::conv>([info](const auto *curr) { return curr->type()->info == info; }, node); conv) {
                return conv->conv(storage.data());
            }
        }

        return {};
    }

    /**
     * @brief Tries to convert an instance to a given type.
     * @tparam Type Type to which to convert the instance.
     * @return True if the conversion is possible, false otherwise.
     */
    template<typename Type>
    bool convert() {
        bool valid = (node && node->info == internal::meta_info<Type>::resolve()->info);

        if(!valid) {
            if(auto any = std::as_const(*this).convert<Type>(); any) {
                swap(any, *this);
                valid = true;
            }
        }

        return valid;
    }

    /**
     * @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) {
        *this = meta_any{std::in_place_type<Type>, std::forward<Args>(args)...};
    }

    /**
     * @brief Aliasing constructor.
     * @return A meta any that shares a reference to an unmanaged object.
     */
    [[nodiscard]] meta_any ref() const ENTT_NOEXCEPT {
        meta_any other{};
        other.node = node;
        other.storage = storage.ref();
        other.deref = deref;
        other.seq_factory = seq_factory;
        other.assoc_factory = assoc_factory;
        return other;
    }

    /**
     * @brief Returns a sequence container proxy.
     * @return A sequence container proxy for the underlying object.
     */
    [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT {
        return seq_factory(storage.data());
    }

    /**
     * @brief Returns an associative container proxy.
     * @return An associative container proxy for the underlying object.
     */
    [[nodiscard]] meta_associative_container as_associative_container() ENTT_NOEXCEPT {
        return assoc_factory(storage.data());
    }

    /**
     * @brief Indirection operator for dereferencing opaque objects.
     * @return A meta any that shares a reference to an unmanaged object if the
     * wrapped element is dereferenceable, an invalid meta any otherwise.
     */
    [[nodiscard]] meta_any operator*() ENTT_NOEXCEPT {
        return deref(*this);
    }

    /**
     * @brief Returns false if a wrapper is empty, true otherwise.
     * @return False if the wrapper is empty, 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 && node->compare(storage.data(), other.storage.data()));
    }

    /**
     * @brief Swaps two meta any objects.
     * @param lhs A valid meta any object.
     * @param rhs A valid meta any object.
     */
    friend void swap(meta_any &lhs, meta_any &rhs) {
        using std::swap;
        swap(lhs.storage, rhs.storage);
        swap(lhs.node, rhs.node);
        swap(lhs.deref, rhs.deref);
        swap(lhs.seq_factory, rhs.seq_factory);
        swap(lhs.assoc_factory, rhs.assoc_factory);
    }

private:
    internal::meta_storage storage;
    internal::meta_type_node *node;
    dereference_operator_type *deref;
    meta_sequence_container(* seq_factory)(void *);
    meta_associative_container(* assoc_factory)(void *);
};





Add Discussion

Log in to comment