Class basic_any

Synopsis

#include <src/entt/core/any.hpp>

template<std::size_t Len, std::size_t Align>
class basic_any

Description

A SBO friendly, type-safe container for single values of any type.

Template Parameters

Len - Size of the storage reserved for the small buffer optimization.

Align - Optional alignment requirement.

Mentioned in

Methods

basic_any overloadDefault constructor.
basic_any overloadConstructs a wrapper by directly initializing the new object.
basic_any overloadConstructs a wrapper from a given value.
basic_any overloadCopy constructor.
basic_any overloadMove constructor.
~basic_anyFrees the internal storage, whatever it means.
as_ref overloadAliasing constructor.
data overloadReturns an opaque pointer to the contained instance.
emplaceReplaces the contained object by creating a new instance directly.
operator boolReturns false if a wrapper is empty, true otherwise.
operator= overloadCopy assignment operator.
operator= overloadMove assignment operator.
operator= overloadValue assignment operator.
operator==Checks if two wrappers differ in their content.
ownerReturns true if a wrapper owns its object, false otherwise.
resetDestroys contained object.
typeReturns the type of the contained object.

Source

Lines 24-329 in src/entt/core/any.hpp.

template<std::size_t Len, std::size_t Align>
class basic_any {
    enum class operation: std::uint8_t { COPY, MOVE, DTOR, COMP, ADDR, CADDR, TYPE };
    enum class policy: std::uint8_t { OWNER, REF, CREF };

    using storage_type = std::aligned_storage_t<Len + !Len, Align>;
    using vtable_type = const void *(const operation, const basic_any &, void *);

    template<typename Type>
    static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v<Type>;

    template<typename Type>
    [[nodiscard]] static constexpr policy type_to_policy() {
        if constexpr(std::is_lvalue_reference_v<Type>) {
            if constexpr(std::is_const_v<std::remove_reference_t<Type>>) {
                return policy::CREF;
            } else {
                return policy::REF;
            }
        } else {
            return policy::OWNER;
        }
    }

    template<typename Type>
    [[nodiscard]] static bool compare(const void *lhs, const void *rhs) {
        if constexpr(!std::is_function_v<Type> && is_equality_comparable_v<Type>) {
            return *static_cast<const Type *>(lhs) == *static_cast<const Type *>(rhs);
        } else {
            return lhs == rhs;
        }
    }

    template<typename Type>
    static const void * basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_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>) {
            const Type *instance = (in_situ<Type> && from.mode == policy::OWNER)
                ? ENTT_LAUNDER(reinterpret_cast<const Type *>(&from.storage))
                : static_cast<const Type *>(from.instance);

            switch(op) {
            case operation::COPY:
                if constexpr(std::is_copy_constructible_v<Type>) {
                    static_cast<basic_any *>(to)->emplace<Type>(*instance);
                }
                break;
            case operation::MOVE:
                if constexpr(in_situ<Type>) {
                    if(from.mode == policy::OWNER) {
                        return new (&static_cast<basic_any *>(to)->storage) Type{std::move(*const_cast<Type *>(instance))};
                    }
                }

                return (static_cast<basic_any *>(to)->instance = std::exchange(const_cast<basic_any &>(from).instance, nullptr));
            case operation::DTOR:
                if(from.mode == policy::OWNER) {
                    if constexpr(in_situ<Type>) {
                        instance->~Type();
                    } else if constexpr(std::is_array_v<Type>) {
                        delete[] instance;
                    } else {
                        delete instance;
                    }
                }
                break;
            case operation::COMP:
                return compare<Type>(instance, (*static_cast<const basic_any **>(to))->data()) ? to : nullptr;
            case operation::ADDR:
                if(from.mode == policy::CREF) {
                    return nullptr;
                }
                [[fallthrough]];
            case operation::CADDR:
                return instance;
            case operation::TYPE:
                *static_cast<type_info *>(to) = type_id<Type>();
                break;
            }
        }

        return nullptr;
    }

    template<typename Type, typename... Args>
    void initialize([[maybe_unused]] Args &&... args) {
        if constexpr(!std::is_void_v<Type>) {
            if constexpr(std::is_lvalue_reference_v<Type>) {
                static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
                instance = (std::addressof(args), ...);
            } else if constexpr(in_situ<Type>) {
                if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
                    new (&storage) Type{std::forward<Args>(args)...};
                } else {
                    new (&storage) Type(std::forward<Args>(args)...);
                }
            } else {
                if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v<Type>) {
                    instance = new Type{std::forward<Args>(args)...};
                } else {
                    instance = new Type(std::forward<Args>(args)...);
                }
            }
        }
    }

    basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT
        : instance{other.data()},
          vtable{other.vtable},
          mode{pol}
    {}

public:
    /*! @brief Size of the internal storage. */
    static constexpr auto length = Len;
    /*! @brief Alignment requirement. */
    static constexpr auto alignment = Align;

    /*! @brief Default constructor. */
    basic_any() ENTT_NOEXCEPT
        : instance{},
          vtable{&basic_vtable<void>},
          mode{policy::OWNER}
    {}

    /**
     * @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 basic_any(std::in_place_type_t<Type>, Args &&... args)
        : instance{},
          vtable{&basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>},
          mode{type_to_policy<Type>()}
    {
        initialize<Type>(std::forward<Args>(args)...);
    }

    /**
     * @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>, basic_any>>>
    basic_any(Type &&value)
        : instance{},
          vtable{&basic_vtable<std::decay_t<Type>>},
          mode{policy::OWNER}
    {
        initialize<std::decay_t<Type>>(std::forward<Type>(value));
    }

    /**
     * @brief Copy constructor.
     * @param other The instance to copy from.
     */
    basic_any(const basic_any &other)
        : instance{},
          vtable{&basic_vtable<void>},
          mode{policy::OWNER}
    {
        other.vtable(operation::COPY, other, this);
    }

    /**
     * @brief Move constructor.
     * @param other The instance to move from.
     */
    basic_any(basic_any &&other) ENTT_NOEXCEPT
        : instance{},
          vtable{other.vtable},
          mode{other.mode}
    {
        vtable(operation::MOVE, other, this);
    }

    /*! @brief Frees the internal storage, whatever it means. */
    ~basic_any() {
        vtable(operation::DTOR, *this, nullptr);
    }

    /**
     * @brief Copy assignment operator.
     * @param other The instance to copy from.
     * @return This any object.
     */
    basic_any & operator=(const basic_any &other) {
        reset();
        other.vtable(operation::COPY, other, this);
        return *this;
    }

    /**
     * @brief Move assignment operator.
     * @param other The instance to move from.
     * @return This any object.
     */
    basic_any & operator=(basic_any &&other) ENTT_NOEXCEPT {
        std::exchange(vtable, other.vtable)(operation::DTOR, *this, nullptr);
        other.vtable(operation::MOVE, other, this);
        mode = other.mode;
        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 any object.
     */
    template<typename Type>
    std::enable_if_t<!std::is_same_v<std::decay_t<Type>, basic_any>, basic_any &>
    operator=(Type &&value) {
        emplace<std::decay_t<Type>>(std::forward<Type>(value));
        return *this;
    }

    /**
     * @brief Returns the type of the contained object.
     * @return The type of the contained object, if any.
     */
    [[nodiscard]] type_info type() const ENTT_NOEXCEPT {
        type_info info{};
        vtable(operation::TYPE, *this, &info);
        return info;
    }

    /**
     * @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 vtable(operation::CADDR, *this, nullptr);
    }

    /*! @copydoc data */
    [[nodiscard]] void * data() ENTT_NOEXCEPT {
        return const_cast<void *>(vtable(operation::ADDR, *this, nullptr));
    }

    /**
     * @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) {
        std::exchange(vtable, &basic_vtable<std::remove_const_t<std::remove_reference_t<Type>>>)(operation::DTOR, *this, nullptr);
        mode = type_to_policy<Type>();
        initialize<Type>(std::forward<Args>(args)...);
    }

    /*! @brief Destroys contained object */
    void reset() {
        std::exchange(vtable, &basic_vtable<void>)(operation::DTOR, *this, nullptr);
        mode = policy::OWNER;
    }

    /**
     * @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 !(vtable(operation::CADDR, *this, nullptr) == 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.
     */
    bool operator==(const basic_any &other) const ENTT_NOEXCEPT {
        const basic_any *trampoline = &other;
        return type() == other.type() && (vtable(operation::COMP, *this, &trampoline) || !other.data());
    }

    /**
     * @brief Aliasing constructor.
     * @return A wrapper that shares a reference to an unmanaged object.
     */
    [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT {
        return basic_any{*this, (mode == policy::CREF ? policy::CREF : policy::REF)};
    }

    /*! @copydoc as_ref */
    [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT {
        return basic_any{*this, policy::CREF};
    }

    /**
     * @brief Returns true if a wrapper owns its object, false otherwise.
     * @return True if the wrapper owns its object, false otherwise.
     */
    [[nodiscard]] bool owner() const ENTT_NOEXCEPT {
        return (mode == policy::OWNER);
    }

private:
    union { const void *instance; storage_type storage; };
    vtable_type *vtable;
    policy mode;
};





Add Discussion

Log in to comment