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.
assign overloadAssigns a value to the contained object without replacing it.
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 object type if any, type_id<void>() otherwise.

Source

Lines 21-391 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,
        transfer,
        assign,
        destroy,
        compare,
        get
    };

    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 &, const 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>
    static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] const void *other) {
        static_assert(!std::is_same_v<Type, void> && std::is_same_v<std::remove_cv_t<std::remove_reference_t<Type>>, Type>, "Invalid type");
        const Type *element = nullptr;

        if constexpr(in_situ<Type>) {
            element = value.owner() ? reinterpret_cast<const Type *>(&value.storage) : static_cast<const Type *>(value.instance);
        } else {
            element = static_cast<const Type *>(value.instance);
        }

        switch(op) {
        case operation::copy:
            if constexpr(std::is_copy_constructible_v<Type>) {
                static_cast<basic_any *>(const_cast<void *>(other))->initialize<Type>(*element);
            }
            break;
        case operation::move:
            if constexpr(in_situ<Type>) {
                if(value.owner()) {
                    return new(&static_cast<basic_any *>(const_cast<void *>(other))->storage) Type{std::move(*const_cast<Type *>(element))};
                }
            }

            return (static_cast<basic_any *>(const_cast<void *>(other))->instance = std::exchange(const_cast<basic_any &>(value).instance, nullptr));
        case operation::transfer:
            if constexpr(std::is_move_assignable_v<Type>) {
                *const_cast<Type *>(element) = std::move(*static_cast<Type *>(const_cast<void *>(other)));
                return other;
            }
            [[fallthrough]];
        case operation::assign:
            if constexpr(std::is_copy_assignable_v<Type>) {
                *const_cast<Type *>(element) = *static_cast<const Type *>(other);
                return other;
            }
            break;
        case operation::destroy:
            if constexpr(in_situ<Type>) {
                element->~Type();
            } else if constexpr(std::is_array_v<Type>) {
                delete[] element;
            } else {
                delete element;
            }
            break;
        case operation::compare:
            if constexpr(!std::is_function_v<Type> && !std::is_array_v<Type> && is_equality_comparable_v<Type>) {
                return *static_cast<const Type *>(element) == *static_cast<const Type *>(other) ? other : nullptr;
            } else {
                return (element == other) ? other : nullptr;
            }
        case operation::get:
            return element;
        }

        return nullptr;
    }

    template<typename Type, typename... Args>
    void initialize([[maybe_unused]] Args &&...args) {
        if constexpr(!std::is_void_v<Type>) {
            info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
            vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;

            if constexpr(std::is_lvalue_reference_v<Type>) {
                static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v<Args> && ...), "Invalid arguments");
                mode = std::is_const_v<std::remove_reference_t<Type>> ? policy::cref : policy::ref;
                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()},
          info{other.info},
          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. */
    constexpr basic_any() ENTT_NOEXCEPT
        : instance{},
          info{&type_id<void>()},
          vtable{},
          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)
        : basic_any{} {
        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)
        : basic_any{} {
        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)
        : basic_any{} {
        if(other.vtable) {
            other.vtable(operation::copy, other, this);
        }
    }

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

    /*! @brief Frees the internal storage, whatever it means. */
    ~basic_any() {
        if(vtable && owner()) {
            vtable(operation::destroy, *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();

        if(other.vtable) {
            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 {
        reset();

        if(other.vtable) {
            other.vtable(operation::move, other, this);
            info = other.info;
            vtable = other.vtable;
            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 object type if any, `type_id<void>()` otherwise.
     * @return The object type if any, `type_id<void>()` otherwise.
     */
    [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT {
        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 ? vtable(operation::get, *this, nullptr) : nullptr;
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @param req Expected type.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] const void *data(const type_info &req) const ENTT_NOEXCEPT {
        return *info == req ? data() : nullptr;
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] void *data() ENTT_NOEXCEPT {
        return (!vtable || mode == policy::cref) ? nullptr : const_cast<void *>(vtable(operation::get, *this, nullptr));
    }

    /**
     * @brief Returns an opaque pointer to the contained instance.
     * @param req Expected type.
     * @return An opaque pointer the contained instance, if any.
     */
    [[nodiscard]] void *data(const type_info &req) ENTT_NOEXCEPT {
        return *info == req ? data() : 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) {
        reset();
        initialize<Type>(std::forward<Args>(args)...);
    }

    /**
     * @brief Assigns a value to the contained object without replacing it.
     * @param other The value to assign to the contained object.
     * @return True in case of success, false otherwise.
     */
    bool assign(const any &other) {
        if(vtable && mode != policy::cref && *info == *other.info) {
            return (vtable(operation::assign, *this, other.data()) != nullptr);
        }

        return false;
    }

    /*! @copydoc assign */
    bool assign(any &&other) {
        if(vtable && mode != policy::cref && *info == *other.info) {
            if(auto *val = other.data(); val) {
                return (vtable(operation::transfer, *this, val) != nullptr);
            } else {
                return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr);
            }
        }

        return false;
    }

    /*! @brief Destroys contained object */
    void reset() {
        if(vtable && owner()) {
            vtable(operation::destroy, *this, nullptr);
        }

        info = &type_id<void>();
        vtable = 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 != 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 {
        if(vtable && *info == *other.info) {
            return (vtable(operation::compare, *this, other.data()) != nullptr);
        }

        return (!vtable && !other.vtable);
    }

    /**
     * @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;
    };
    const type_info *info;
    vtable_type *vtable;
    policy mode;
};





Add Discussion

Log in to comment