| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521 |
- #ifndef ENTT_CORE_ANY_HPP
- #define ENTT_CORE_ANY_HPP
- #include <cstddef>
- #include <memory>
- #include <type_traits>
- #include <utility>
- #include "../config/config.h"
- #include "../core/utility.hpp"
- #include "fwd.hpp"
- #include "type_info.hpp"
- #include "type_traits.hpp"
- namespace entt {
- /**
- * @cond TURN_OFF_DOXYGEN
- * Internal details not to be documented.
- */
- namespace internal {
- enum class any_operation : std::uint8_t {
- copy,
- move,
- transfer,
- assign,
- destroy,
- compare,
- get
- };
- } // namespace internal
- /**
- * Internal details not to be documented.
- * @endcond
- */
- /*! @brief Possible modes of an any object. */
- enum class any_policy : std::uint8_t {
- /*! @brief Default mode, the object owns the contained element. */
- owner,
- /*! @brief Aliasing mode, the object _points_ to a non-const element. */
- ref,
- /*! @brief Const aliasing mode, the object _points_ to a const element. */
- cref
- };
- /**
- * @brief A SBO friendly, type-safe container for single values of any type.
- * @tparam Len Size of the storage reserved for the small buffer optimization.
- * @tparam Align Optional alignment requirement.
- */
- template<std::size_t Len, std::size_t Align>
- class basic_any {
- using operation = internal::any_operation;
- using vtable_type = const void *(const operation, const basic_any &, const void *);
- struct storage_type {
- alignas(Align) std::byte data[Len + !Len];
- };
- template<typename Type>
- static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v<Type>;
- template<typename Type>
- static const void *basic_vtable(const operation op, const basic_any &value, const void *other) {
- static_assert(!std::is_void_v<Type> && 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.mode == any_policy::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.mode == any_policy::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 *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) {
- info = &type_id<std::remove_cv_t<std::remove_reference_t<Type>>>();
- if constexpr(!std::is_void_v<Type>) {
- vtable = basic_vtable<std::remove_cv_t<std::remove_reference_t<Type>>>;
- if constexpr(std::is_lvalue_reference_v<Type>) {
- static_assert((std::is_lvalue_reference_v<Args> && ...) && (sizeof...(Args) == 1u), "Invalid arguments");
- mode = std::is_const_v<std::remove_reference_t<Type>> ? any_policy::cref : any_policy::ref;
- instance = (std::addressof(args), ...);
- } else if constexpr(in_situ<std::remove_cv_t<std::remove_reference_t<Type>>>) {
- if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
- new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
- } else {
- new(&storage) std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
- }
- } else {
- if constexpr(std::is_aggregate_v<std::remove_cv_t<std::remove_reference_t<Type>>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>)) {
- instance = new std::remove_cv_t<std::remove_reference_t<Type>>{std::forward<Args>(args)...};
- } else {
- instance = new std::remove_cv_t<std::remove_reference_t<Type>>(std::forward<Args>(args)...);
- }
- }
- }
- }
- basic_any(const basic_any &other, const any_policy pol) 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() noexcept
- : basic_any{std::in_place_type<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 basic_any(std::in_place_type_t<Type>, Args &&...args)
- : instance{},
- info{},
- vtable{},
- mode{any_policy::owner} {
- 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{std::in_place_type<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) 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 && (mode == any_policy::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) 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 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 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 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() noexcept {
- return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data());
- }
- /**
- * @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) noexcept {
- return mode == any_policy::cref ? nullptr : const_cast<void *>(std::as_const(*this).data(req));
- }
- /**
- * @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 basic_any &other) {
- if(vtable && mode != any_policy::cref && *info == *other.info) {
- return (vtable(operation::assign, *this, other.data()) != nullptr);
- }
- return false;
- }
- /*! @copydoc assign */
- bool assign(basic_any &&other) {
- if(vtable && mode != any_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 && (mode == any_policy::owner)) {
- vtable(operation::destroy, *this, nullptr);
- }
- // unnecessary but it helps to detect nasty bugs
- ENTT_ASSERT((instance = nullptr) == nullptr, "");
- info = &type_id<void>();
- vtable = nullptr;
- mode = any_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 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.
- */
- [[nodiscard]] bool operator==(const basic_any &other) const noexcept {
- if(vtable && *info == *other.info) {
- return (vtable(operation::compare, *this, other.data()) != nullptr);
- }
- return (!vtable && !other.vtable);
- }
- /**
- * @brief Checks if two wrappers differ in their content.
- * @param other Wrapper with which to compare.
- * @return True if the two objects differ in their content, false otherwise.
- */
- [[nodiscard]] bool operator!=(const basic_any &other) const noexcept {
- return !(*this == other);
- }
- /**
- * @brief Aliasing constructor.
- * @return A wrapper that shares a reference to an unmanaged object.
- */
- [[nodiscard]] basic_any as_ref() noexcept {
- return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)};
- }
- /*! @copydoc as_ref */
- [[nodiscard]] basic_any as_ref() const noexcept {
- return basic_any{*this, any_policy::cref};
- }
- /**
- * @brief Returns true if a wrapper owns its object, false otherwise.
- * @return True if the wrapper owns its object, false otherwise.
- */
- [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept {
- return (mode == any_policy::owner);
- }
- /**
- * @brief Returns the current mode of an any object.
- * @return The current mode of the any object.
- */
- [[nodiscard]] any_policy policy() const noexcept {
- return mode;
- }
- private:
- union {
- const void *instance;
- storage_type storage;
- };
- const type_info *info;
- vtable_type *vtable;
- any_policy mode;
- };
- /**
- * @brief Performs type-safe access to the contained object.
- * @tparam Type Type to which conversion is required.
- * @tparam Len Size of the storage reserved for the small buffer optimization.
- * @tparam Align Alignment requirement.
- * @param data Target any object.
- * @return The element converted to the requested type.
- */
- template<typename Type, std::size_t Len, std::size_t Align>
- [[nodiscard]] Type any_cast(const basic_any<Len, Align> &data) noexcept {
- const auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
- ENTT_ASSERT(instance, "Invalid instance");
- return static_cast<Type>(*instance);
- }
- /*! @copydoc any_cast */
- template<typename Type, std::size_t Len, std::size_t Align>
- [[nodiscard]] Type any_cast(basic_any<Len, Align> &data) noexcept {
- // forces const on non-reference types to make them work also with wrappers for const references
- auto *const instance = any_cast<std::remove_reference_t<const Type>>(&data);
- ENTT_ASSERT(instance, "Invalid instance");
- return static_cast<Type>(*instance);
- }
- /*! @copydoc any_cast */
- template<typename Type, std::size_t Len, std::size_t Align>
- [[nodiscard]] Type any_cast(basic_any<Len, Align> &&data) noexcept {
- if constexpr(std::is_copy_constructible_v<std::remove_cv_t<std::remove_reference_t<Type>>>) {
- if(auto *const instance = any_cast<std::remove_reference_t<Type>>(&data); instance) {
- return static_cast<Type>(std::move(*instance));
- } else {
- return any_cast<Type>(data);
- }
- } else {
- auto *const instance = any_cast<std::remove_reference_t<Type>>(&data);
- ENTT_ASSERT(instance, "Invalid instance");
- return static_cast<Type>(std::move(*instance));
- }
- }
- /*! @copydoc any_cast */
- template<typename Type, std::size_t Len, std::size_t Align>
- [[nodiscard]] const Type *any_cast(const basic_any<Len, Align> *data) noexcept {
- const auto &info = type_id<std::remove_cv_t<Type>>();
- return static_cast<const Type *>(data->data(info));
- }
- /*! @copydoc any_cast */
- template<typename Type, std::size_t Len, std::size_t Align>
- [[nodiscard]] Type *any_cast(basic_any<Len, Align> *data) noexcept {
- if constexpr(std::is_const_v<Type>) {
- // last attempt to make wrappers for const references return their values
- return any_cast<Type>(&std::as_const(*data));
- } else {
- const auto &info = type_id<std::remove_cv_t<Type>>();
- return static_cast<Type *>(data->data(info));
- }
- }
- /**
- * @brief Constructs a wrapper from a given type, passing it all arguments.
- * @tparam Type Type of object to use to initialize the wrapper.
- * @tparam Len Size of the storage reserved for the small buffer optimization.
- * @tparam Align Optional alignment requirement.
- * @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<typename Type, std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename... Args>
- [[nodiscard]] basic_any<Len, Align> make_any(Args &&...args) {
- return basic_any<Len, Align>{std::in_place_type<Type>, std::forward<Args>(args)...};
- }
- /**
- * @brief Forwards its argument and avoids copies for lvalue references.
- * @tparam Len Size of the storage reserved for the small buffer optimization.
- * @tparam Align Optional alignment requirement.
- * @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<std::size_t Len = basic_any<>::length, std::size_t Align = basic_any<Len>::alignment, typename Type>
- [[nodiscard]] basic_any<Len, Align> forward_as_any(Type &&value) {
- return basic_any<Len, Align>{std::in_place_type<Type &&>, std::forward<Type>(value)};
- }
- } // namespace entt
- #endif
|