// #include "config/version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 8 #define ENTT_VERSION_PATCH 0 #endif // #include "core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP #include #include #include #include #include // #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif namespace entt { /** * @brief Function object to wrap `std::sort` in a class type. * * Unfortunately, `std::sort` cannot be passed as template argument to a class * template or a function template.
* This class fills the gap by wrapping some flavors of `std::sort` in a * function object. */ struct std_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @tparam Args Types of arguments to forward to the sort function. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. * @param args Arguments to forward to the sort function, if any. */ template, typename... Args> void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); } }; /*! @brief Function object for performing insertion sort. */ struct insertion_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. */ template> void operator()(It first, It last, Compare compare = Compare{}) const { if (first < last) { for (auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; for (; pre > first && compare(value, *(pre - 1)); --pre) { *pre = std::move(*(pre - 1)); } *pre = std::move(value); } } } }; /** * @brief Function object for performing LSD radix sort. * @tparam Bit Number of bits processed per pass. * @tparam N Maximum number of bits to sort. */ template struct radix_sort { static_assert((N% Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given _getter_ to access the * actual data to be sorted. * * This implementation is inspired by the online book * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). * * @tparam It Type of random access iterator. * @tparam Getter Type of _getter_ function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param getter A valid _getter_ function object. */ template void operator()(It first, It last, Getter getter = Getter{}) const { if (first < last) { static constexpr auto mask = (1 << Bit) - 1; static constexpr auto buckets = 1 << Bit; static constexpr auto passes = N / Bit; using value_type = typename std::iterator_traits::value_type; std::vector aux(std::distance(first, last)); auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { std::size_t index[buckets]{}; std::size_t count[buckets]{}; for (auto it = from; it != to; ++it) { ++count[(getter(*it) >> start) & mask]; } for (std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { index[pos + 1u] = index[pos] + count[pos]; } for (auto it = from; it != to; ++it) { out[index[(getter(*it) >> start) & mask]++] = std::move(*it); } }; for (std::size_t pass = 0; pass < (passes & ~1); pass += 2) { part(first, last, aux.begin(), pass * Bit); part(aux.begin(), aux.end(), first, (pass + 1) * Bit); } if constexpr (passes & 1) { part(first, last, aux.begin(), (passes - 1) * Bit); std::move(aux.begin(), aux.end(), first); } } } }; } #endif // #include "core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif namespace entt { /** * @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 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; using vtable_type = const void* (const operation, const basic_any&, void*); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template [[nodiscard]] static constexpr policy type_to_policy() { if constexpr (std::is_lvalue_reference_v) { if constexpr (std::is_const_v>) { return policy::CREF; } else { return policy::REF; } } else { return policy::OWNER; } } template [[nodiscard]] static bool compare(const void* lhs, const void* rhs) { if constexpr (!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); } else { return lhs == rhs; } } template 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>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { const Type* instance = (in_situ && from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); switch (op) { case operation::COPY: if constexpr (std::is_copy_constructible_v) { static_cast(to)->emplace(*instance); } break; case operation::MOVE: if constexpr (in_situ) { if (from.mode == policy::OWNER) { return new (&static_cast(to)->storage) Type{ std::move(*const_cast(instance)) }; } } return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); case operation::DTOR: if (from.mode == policy::OWNER) { if constexpr (in_situ) { instance->~Type(); } else if constexpr (std::is_array_v) { delete[] instance; } else { delete instance; } } break; case operation::COMP: return compare(instance, (*static_cast(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(to) = type_id(); break; } } return nullptr; } template void initialize([[maybe_unused]] Args &&... args) { if constexpr (!std::is_void_v) { if constexpr (std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); instance = (std::addressof(args), ...); } else if constexpr (in_situ) { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { new (&storage) Type{ std::forward(args)... }; } else { new (&storage) Type(std::forward(args)...); } } else { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { instance = new Type{ std::forward(args)... }; } else { instance = new Type(std::forward(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 }, 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 explicit basic_any(std::in_place_type_t, Args &&... args) : instance{}, vtable{ &basic_vtable>> }, mode{ type_to_policy() } { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper 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 basic_any(std::reference_wrapper value) ENTT_NOEXCEPT : basic_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, basic_any>>> basic_any(Type&& value) : instance{}, vtable{ &basic_vtable> }, mode{ policy::OWNER } { initialize>(std::forward(value)); } /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any& other) : instance{}, vtable{ &basic_vtable }, 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 [[deprecated("Use std::in_place_type, entt::make_any, emplace or forward_as_any instead")]] basic_any& operator=(std::reference_wrapper value) ENTT_NOEXCEPT { emplace(value.get()); 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 std::enable_if_t, basic_any>, basic_any&> operator=(Type&& value) { emplace>(std::forward(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(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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, *this, nullptr); mode = type_to_policy(); initialize(std::forward(args)...); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(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; }; /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ template [[nodiscard]] inline bool operator!=(const basic_any& lhs, const basic_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @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 Type any_cast(const basic_any& data) ENTT_NOEXCEPT { const auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any&& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } /*! @copydoc any_cast */ template const Type* any_cast(const basic_any* data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ template Type* any_cast(basic_any* data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } /** * @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::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&... args) { return basic_any{std::in_place_type, std::forward(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::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type&& value) { return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } } #endif // #include "core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "core/family.hpp" #ifndef ENTT_CORE_FAMILY_HPP #define ENTT_CORE_FAMILY_HPP // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Dynamic identifier generator. * * Utility class template that can be used to assign unique identifiers to types * at runtime. Use different specializations to create separate sets of * identifiers. */ template class family { inline static ENTT_MAYBE_ATOMIC(id_type) identifier {}; public: /*! @brief Unsigned integer type. */ using family_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type inline static const family_type type = identifier++; }; } #endif // #include "core/hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "core/ident.hpp" #ifndef ENTT_CORE_IDENT_HPP #define ENTT_CORE_IDENT_HPP #include #include #include // #include "../config/config.h" // #include "fwd.hpp" // #include "type_traits.hpp" namespace entt { /** * @brief Types identifiers. * * Variable template used to generate identifiers at compile-time for the given * types. Use the `get` member function to know what's the identifier associated * to the specific type. * * @note * Identifiers are constant expression and can be used in any context where such * an expression is required. As an example: * @code{.cpp} * using id = entt::identifier; * * switch(a_type_identifier) { * case id::type: * // ... * break; * case id::type: * // ... * break; * default: * // ... * } * @endcode * * @tparam Types List of types for which to generate identifiers. */ template class identifier { template [[nodiscard]] static constexpr id_type get(std::index_sequence) { static_assert(std::disjunction_v...>, "Invalid type"); return (0 + ... + (std::is_same_v...>>> ? id_type{ Index } : id_type{})); } public: /*! @brief Unsigned integer type. */ using identifier_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template static constexpr identifier_type type = get>(std::index_sequence_for{}); }; } #endif // #include "core/monostate.hpp" #ifndef ENTT_CORE_MONOSTATE_HPP #define ENTT_CORE_MONOSTATE_HPP // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Minimal implementation of the monostate pattern. * * A minimal, yet complete configuration system built on top of the monostate * pattern. Thread safe by design, it works only with basic types like `int`s or * `bool`s.
* Multiple types and therefore more than one value can be associated with a * single key. Because of this, users must pay attention to use the same type * both during an assignment and when they try to read back their data. * Otherwise, they can incur in unexpected results. */ template struct monostate { /** * @brief Assigns a value of a specific type to a given key. * @tparam Type Type of the value to assign. * @param val User data to assign to the given key. */ template void operator=(Type val) const ENTT_NOEXCEPT { value = val; } /** * @brief Gets a value of a specific type for a given key. * @tparam Type Type of the value to get. * @return Stored value, if any. */ template operator Type() const ENTT_NOEXCEPT { return value; } private: template inline static ENTT_MAYBE_ATOMIC(Type) value {}; }; /** * @brief Helper variable template. * @tparam Value Value used to differentiate between different variables. */ template inline monostate monostate_v = {}; } #endif // #include "core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "hashed_string.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "entity/component.hpp" #ifndef ENTT_ENTITY_COMPONENT_HPP #define ENTT_ENTITY_COMPONENT_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Commonly used default traits for all types. */ struct basic_component_traits { /*! @brief Pointer stability, default is `std::false_type`. */ using in_place_delete = std::false_type; /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */ using ignore_if_empty = ENTT_IGNORE_IF_EMPTY; }; /** * @brief Common way to access various properties of components. * @tparam Type Type of component. */ template struct component_traits : basic_component_traits { static_assert(std::is_same_v, Type>, "Unsupported type"); }; } #endif // #include "entity/entity.hpp" #ifndef ENTT_ENTITY_ENTITY_HPP #define ENTT_ENTITY_ENTITY_HPP #include #include #include // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct entt_traits; template struct entt_traits>> : entt_traits> {}; template struct entt_traits>> : entt_traits {}; template<> struct entt_traits { using entity_type = std::uint32_t; using version_type = std::uint16_t; using difference_type = std::int64_t; static constexpr entity_type entity_mask = 0xFFFFF; static constexpr entity_type version_mask = 0xFFF; static constexpr std::size_t entity_shift = 20u; }; template<> struct entt_traits { using entity_type = std::uint64_t; using version_type = std::uint32_t; using difference_type = std::int64_t; static constexpr entity_type entity_mask = 0xFFFFFFFF; static constexpr entity_type version_mask = 0xFFFFFFFF; static constexpr std::size_t entity_shift = 32u; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Entity traits. * @tparam Type Type of identifier. */ template class entt_traits : private internal::entt_traits { using traits_type = internal::entt_traits; public: /*! @brief Value type. */ using value_type = Type; /*! @brief Underlying entity type. */ using entity_type = typename traits_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename traits_type::version_type; /*! @brief Difference type. */ using difference_type = typename traits_type::difference_type; /** * @brief Converts an entity to its underlying type. * @param value The value to convert. * @return The integral representation of the given value. */ [[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT { return static_cast(value); } /** * @brief Returns the entity part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the entity part. */ [[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { return (to_integral(value) & traits_type::entity_mask); } /** * @brief Returns the version part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the version part. */ [[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT { constexpr auto mask = (traits_type::version_mask << traits_type::entity_shift); return ((to_integral(value) & mask) >> traits_type::entity_shift); } /** * @brief Constructs an identifier from its parts. * * If the version part is not provided, a tombstone is returned.
* If the entity part is not provided, a null identifier is returned. * * @param entity The entity part of the identifier. * @param version The version part of the identifier. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type construct(const entity_type entity = traits_type::entity_mask, const version_type version = traits_type::version_mask) ENTT_NOEXCEPT { return value_type{ (entity & traits_type::entity_mask) | (version << traits_type::entity_shift) }; } }; /** * @brief Converts an entity to its underlying type. * @tparam Entity The value type. * @param entity The value to convert. * @return The integral representation of the given value. */ template [[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { return entt_traits::to_integral(entity); } /*! @brief Null object for all entity identifiers. */ struct null_t { /** * @brief Converts the null object to identifiers of any type. * @tparam Entity Type of entity identifier. * @return The null representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { return entt_traits::construct(); } /** * @brief Compares two null objects. * @param other A null object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { return true; } /** * @brief Compares two null objects. * @param other A null object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { return false; } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::to_entity(entity) == entt_traits::to_entity(*this); } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { return !(entity == *this); } /** * @brief Creates a null object from an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier to turn into a null object. * @return The null representation for the given identifier. */ template [[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::construct(entt_traits::to_entity(*this), entt_traits::to_version(entity)); } }; /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A null object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { return other.operator==(entity); } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A null object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { return !(other == entity); } /*! @brief Tombstone object for all entity identifiers. */ struct tombstone_t { /** * @brief Converts the tombstone object to identifiers of any type. * @tparam Entity Type of entity identifier. * @return The tombstone representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { return entt_traits::construct(); } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { return true; } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { return false; } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::to_version(entity) == entt_traits::to_version(*this); } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { return !(entity == *this); } /** * @brief Creates a tombstone object from an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier to turn into a tombstone object. * @return The tombstone representation for the given identifier. */ template [[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::construct(entt_traits::to_entity(entity)); } }; /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A tombstone object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { return other.operator==(entity); } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A tombstone object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { return !(other == entity); } /** * @brief Compile-time constant for null entities. * * There exist implicit conversions from this variable to entity identifiers of * any allowed type. Similarly, there exist comparision operators between the * null entity and any other entity identifier. */ inline constexpr null_t null{}; /** * @brief Compile-time constant for tombstone entities. * * There exist implicit conversions from this variable to entity identifiers of * any allowed type. Similarly, there exist comparision operators between the * tombstone entity and any other entity identifier. */ inline constexpr tombstone_t tombstone{}; } #endif // #include "entity/group.hpp" #ifndef ENTT_ENTITY_GROUP_HPP #define ENTT_ENTITY_GROUP_HPP #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "entity.hpp" #ifndef ENTT_ENTITY_ENTITY_HPP #define ENTT_ENTITY_ENTITY_HPP #include #include #include // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct entt_traits; template struct entt_traits>> : entt_traits> {}; template struct entt_traits>> : entt_traits {}; template<> struct entt_traits { using entity_type = std::uint32_t; using version_type = std::uint16_t; using difference_type = std::int64_t; static constexpr entity_type entity_mask = 0xFFFFF; static constexpr entity_type version_mask = 0xFFF; static constexpr std::size_t entity_shift = 20u; }; template<> struct entt_traits { using entity_type = std::uint64_t; using version_type = std::uint32_t; using difference_type = std::int64_t; static constexpr entity_type entity_mask = 0xFFFFFFFF; static constexpr entity_type version_mask = 0xFFFFFFFF; static constexpr std::size_t entity_shift = 32u; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Entity traits. * @tparam Type Type of identifier. */ template class entt_traits : private internal::entt_traits { using traits_type = internal::entt_traits; public: /*! @brief Value type. */ using value_type = Type; /*! @brief Underlying entity type. */ using entity_type = typename traits_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename traits_type::version_type; /*! @brief Difference type. */ using difference_type = typename traits_type::difference_type; /** * @brief Converts an entity to its underlying type. * @param value The value to convert. * @return The integral representation of the given value. */ [[nodiscard]] static constexpr entity_type to_integral(const value_type value) ENTT_NOEXCEPT { return static_cast(value); } /** * @brief Returns the entity part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the entity part. */ [[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { return (to_integral(value) & traits_type::entity_mask); } /** * @brief Returns the version part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the version part. */ [[nodiscard]] static constexpr version_type to_version(const value_type value) ENTT_NOEXCEPT { constexpr auto mask = (traits_type::version_mask << traits_type::entity_shift); return ((to_integral(value) & mask) >> traits_type::entity_shift); } /** * @brief Constructs an identifier from its parts. * * If the version part is not provided, a tombstone is returned.
* If the entity part is not provided, a null identifier is returned. * * @param entity The entity part of the identifier. * @param version The version part of the identifier. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type construct(const entity_type entity = traits_type::entity_mask, const version_type version = traits_type::version_mask) ENTT_NOEXCEPT { return value_type{ (entity & traits_type::entity_mask) | (version << traits_type::entity_shift) }; } }; /** * @brief Converts an entity to its underlying type. * @tparam Entity The value type. * @param entity The value to convert. * @return The integral representation of the given value. */ template [[nodiscard]] constexpr auto to_integral(const Entity entity) ENTT_NOEXCEPT { return entt_traits::to_integral(entity); } /*! @brief Null object for all entity identifiers. */ struct null_t { /** * @brief Converts the null object to identifiers of any type. * @tparam Entity Type of entity identifier. * @return The null representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { return entt_traits::construct(); } /** * @brief Compares two null objects. * @param other A null object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { return true; } /** * @brief Compares two null objects. * @param other A null object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { return false; } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::to_entity(entity) == entt_traits::to_entity(*this); } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { return !(entity == *this); } /** * @brief Creates a null object from an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier to turn into a null object. * @return The null representation for the given identifier. */ template [[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::construct(entt_traits::to_entity(*this), entt_traits::to_version(entity)); } }; /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A null object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) ENTT_NOEXCEPT { return other.operator==(entity); } /** * @brief Compares a null object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A null object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) ENTT_NOEXCEPT { return !(other == entity); } /*! @brief Tombstone object for all entity identifiers. */ struct tombstone_t { /** * @brief Converts the tombstone object to identifiers of any type. * @tparam Entity Type of entity identifier. * @return The tombstone representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const ENTT_NOEXCEPT { return entt_traits::construct(); } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { return true; } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { return false; } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::to_version(entity) == entt_traits::to_version(*this); } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { return !(entity == *this); } /** * @brief Creates a tombstone object from an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier to turn into a tombstone object. * @return The tombstone representation for the given identifier. */ template [[nodiscard]] constexpr Entity operator|(const Entity entity) const ENTT_NOEXCEPT { return entt_traits::construct(entt_traits::to_entity(entity)); } }; /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A tombstone object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { return other.operator==(entity); } /** * @brief Compares a tombstone object and an entity identifier of any type. * @tparam Entity Type of entity identifier. * @param entity Entity identifier with which to compare. * @param other A tombstone object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) ENTT_NOEXCEPT { return !(other == entity); } /** * @brief Compile-time constant for null entities. * * There exist implicit conversions from this variable to entity identifiers of * any allowed type. Similarly, there exist comparision operators between the * null entity and any other entity identifier. */ inline constexpr null_t null{}; /** * @brief Compile-time constant for tombstone entities. * * There exist implicit conversions from this variable to entity identifiers of * any allowed type. Similarly, there exist comparision operators between the * tombstone entity and any other entity identifier. */ inline constexpr tombstone_t tombstone{}; } #endif // #include "fwd.hpp" #ifndef ENTT_ENTITY_FWD_HPP #define ENTT_ENTITY_FWD_HPP #include // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif namespace entt { template> class basic_sparse_set; template> struct basic_storage; template class basic_registry; template struct basic_view; template class basic_runtime_view; template class basic_group; template class basic_observer; template class basic_organizer; template struct basic_handle; template class basic_snapshot; template class basic_snapshot_loader; template class basic_continuous_loader; /*! @brief Default entity identifier. */ enum class entity : id_type {}; /*! @brief Alias declaration for the most common use case. */ using sparse_set = basic_sparse_set; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using storage = basic_storage; /*! @brief Alias declaration for the most common use case. */ using registry = basic_registry; /*! @brief Alias declaration for the most common use case. */ using observer = basic_observer; /*! @brief Alias declaration for the most common use case. */ using organizer = basic_organizer; /*! @brief Alias declaration for the most common use case. */ using handle = basic_handle; /*! @brief Alias declaration for the most common use case. */ using const_handle = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using handle_view = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using const_handle_view = basic_handle; /*! @brief Alias declaration for the most common use case. */ using snapshot = basic_snapshot; /*! @brief Alias declaration for the most common use case. */ using snapshot_loader = basic_snapshot_loader; /*! @brief Alias declaration for the most common use case. */ using continuous_loader = basic_continuous_loader; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using view = basic_view; /*! @brief Alias declaration for the most common use case. */ using runtime_view = basic_runtime_view; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using group = basic_group; } #endif // #include "sparse_set.hpp" #ifndef ENTT_ENTITY_SPARSE_SET_HPP #define ENTT_ENTITY_SPARSE_SET_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP #include #include #include #include #include // #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif namespace entt { /** * @brief Function object to wrap `std::sort` in a class type. * * Unfortunately, `std::sort` cannot be passed as template argument to a class * template or a function template.
* This class fills the gap by wrapping some flavors of `std::sort` in a * function object. */ struct std_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @tparam Args Types of arguments to forward to the sort function. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. * @param args Arguments to forward to the sort function, if any. */ template, typename... Args> void operator()(It first, It last, Compare compare = Compare{}, Args &&... args) const { std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); } }; /*! @brief Function object for performing insertion sort. */ struct insertion_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. */ template> void operator()(It first, It last, Compare compare = Compare{}) const { if (first < last) { for (auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; for (; pre > first && compare(value, *(pre - 1)); --pre) { *pre = std::move(*(pre - 1)); } *pre = std::move(value); } } } }; /** * @brief Function object for performing LSD radix sort. * @tparam Bit Number of bits processed per pass. * @tparam N Maximum number of bits to sort. */ template struct radix_sort { static_assert((N% Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given _getter_ to access the * actual data to be sorted. * * This implementation is inspired by the online book * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). * * @tparam It Type of random access iterator. * @tparam Getter Type of _getter_ function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param getter A valid _getter_ function object. */ template void operator()(It first, It last, Getter getter = Getter{}) const { if (first < last) { static constexpr auto mask = (1 << Bit) - 1; static constexpr auto buckets = 1 << Bit; static constexpr auto passes = N / Bit; using value_type = typename std::iterator_traits::value_type; std::vector aux(std::distance(first, last)); auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { std::size_t index[buckets]{}; std::size_t count[buckets]{}; for (auto it = from; it != to; ++it) { ++count[(getter(*it) >> start) & mask]; } for (std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { index[pos + 1u] = index[pos] + count[pos]; } for (auto it = from; it != to; ++it) { out[index[(getter(*it) >> start) & mask]++] = std::move(*it); } }; for (std::size_t pass = 0; pass < (passes & ~1); pass += 2) { part(first, last, aux.begin(), pass * Bit); part(aux.begin(), aux.end(), first, (pass + 1) * Bit); } if constexpr (passes & 1) { part(first, last, aux.begin(), (passes - 1) * Bit); std::move(aux.begin(), aux.end(), first); } } } }; } #endif // #include "../core/fwd.hpp" // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /*! @brief Sparse set deletion policy. */ enum class deletion_policy : std::uint8_t { /*! @brief Swap-and-pop deletion policy. */ swap_and_pop = 0u, /*! @brief In-place deletion policy. */ in_place = 1u }; /** * @brief Basic sparse set implementation. * * Sparse set or packed array or whatever is the name users give it.
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
* This is largely used by the registry to offer users the fastest access ever * to the components. Views and groups in general are almost entirely designed * around sparse sets. * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_sparse_set { static constexpr auto growth_factor = 1.5; static constexpr auto sparse_page = ENTT_SPARSE_PAGE; using traits_type = entt_traits; using alloc_traits = typename std::allocator_traits::template rebind_traits; using alloc_pointer = typename alloc_traits::pointer; using alloc_const_pointer = typename alloc_traits::const_pointer; using bucket_alloc_traits = typename std::allocator_traits::template rebind_traits; using bucket_alloc_pointer = typename bucket_alloc_traits::pointer; static_assert(alloc_traits::propagate_on_container_move_assignment::value); static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value); struct sparse_set_iterator final { using difference_type = typename traits_type::difference_type; using value_type = Entity; using pointer = const value_type*; using reference = const value_type&; using iterator_category = std::random_access_iterator_tag; sparse_set_iterator() ENTT_NOEXCEPT = default; sparse_set_iterator(const alloc_const_pointer* ref, const difference_type idx) ENTT_NOEXCEPT : packed{ ref }, index{ idx } {} sparse_set_iterator& operator++() ENTT_NOEXCEPT { return --index, * this; } sparse_set_iterator operator++(int) ENTT_NOEXCEPT { iterator orig = *this; return ++(*this), orig; } sparse_set_iterator& operator--() ENTT_NOEXCEPT { return ++index, * this; } sparse_set_iterator operator--(int) ENTT_NOEXCEPT { sparse_set_iterator orig = *this; return operator--(), orig; } sparse_set_iterator& operator+=(const difference_type value) ENTT_NOEXCEPT { index -= value; return *this; } sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { sparse_set_iterator copy = *this; return (copy += value); } sparse_set_iterator& operator-=(const difference_type value) ENTT_NOEXCEPT { return (*this += -value); } sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { return (*this + -value); } difference_type operator-(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return other.index - index; } [[nodiscard]] reference operator[](const difference_type value) const { const auto pos = size_type(index - value - 1u); return (*packed)[pos]; } [[nodiscard]] bool operator==(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return other.index == index; } [[nodiscard]] bool operator!=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] bool operator<(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return index > other.index; } [[nodiscard]] bool operator>(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return index < other.index; } [[nodiscard]] bool operator<=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this > other); } [[nodiscard]] bool operator>=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this < other); } [[nodiscard]] pointer operator->() const { const auto pos = size_type(index - 1u); return std::addressof((*packed)[pos]); } [[nodiscard]] reference operator*() const { return *operator->(); } private: const alloc_const_pointer* packed; difference_type index; }; [[nodiscard]] static auto page(const Entity entt) ENTT_NOEXCEPT { return size_type{ traits_type::to_entity(entt) / sparse_page }; } [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT { return size_type{ traits_type::to_entity(entt) & (sparse_page - 1) }; } [[nodiscard]] auto assure_page(const std::size_t idx) { if (!(idx < bucket)) { const size_type sz = idx + 1u; const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz); std::uninitialized_value_construct(mem + bucket, mem + sz); std::uninitialized_copy(sparse, sparse + bucket, mem); std::destroy(sparse, sparse + bucket); bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket); sparse = mem; bucket = sz; } if (!sparse[idx]) { sparse[idx] = alloc_traits::allocate(allocator, sparse_page); std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page, null); } return sparse[idx]; } void resize_packed(const std::size_t req) { ENTT_ASSERT((req != reserved) && !(req < count), "Invalid request"); const auto mem = alloc_traits::allocate(allocator, req); std::uninitialized_copy(packed, packed + count, mem); std::uninitialized_fill(mem + count, mem + req, tombstone); std::destroy(packed, packed + reserved); alloc_traits::deallocate(allocator, packed, reserved); packed = mem; reserved = req; } void release_memory() { if (packed) { for (size_type pos{}; pos < bucket; ++pos) { if (sparse[pos]) { std::destroy(sparse[pos], sparse[pos] + sparse_page); alloc_traits::deallocate(allocator, sparse[pos], sparse_page); } } std::destroy(packed, packed + reserved); std::destroy(sparse, sparse + bucket); alloc_traits::deallocate(allocator, packed, reserved); bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket); } } protected: /** * @brief Swaps two entities in the internal packed array. * @param lhs A valid position of an entity within storage. * @param rhs A valid position of an entity within storage. */ virtual void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {} /** * @brief Moves an entity in the internal packed array. * @param from A valid position of an entity within storage. * @param to A valid position of an entity within storage. */ virtual void move_and_pop([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) {} /** * @brief Attempts to erase an entity from the internal packed array. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ virtual void swap_and_pop(const Entity entt, [[maybe_unused]] void* ud) { auto& ref = sparse[page(entt)][offset(entt)]; const auto pos = size_type{ traits_type::to_entity(ref) }; ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier"); auto& last = packed[--count]; packed[pos] = last; sparse[page(last)][offset(last)] = ref; // lazy self-assignment guard ref = null; // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((last = tombstone, true), ""); } /** * @brief Attempts to erase an entity from the internal packed array. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ virtual void in_place_pop(const Entity entt, [[maybe_unused]] void* ud) { auto& ref = sparse[page(entt)][offset(entt)]; const auto pos = size_type{ traits_type::to_entity(ref) }; ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier"); packed[pos] = std::exchange(free_list, traits_type::construct(static_cast(pos))); // lazy self-assignment guard ref = null; } public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = alloc_const_pointer; /*! @brief Random access iterator type. */ using iterator = sparse_set_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /** * @brief Constructs an empty container with the given policy and allocator. * @param pol Type of deletion policy. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_sparse_set(deletion_policy pol, const allocator_type& alloc = {}) : allocator{ alloc }, bucket_allocator{ alloc }, sparse{ bucket_alloc_traits::allocate(bucket_allocator, 0u) }, packed{ alloc_traits::allocate(allocator, 0u) }, bucket{ 0u }, count{ 0u }, reserved{ 0u }, free_list{ tombstone }, mode{ pol } {} /** * @brief Constructs an empty container with the given allocator. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_sparse_set(const allocator_type& alloc = {}) : basic_sparse_set{ deletion_policy::swap_and_pop, alloc } {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_sparse_set(basic_sparse_set&& other) ENTT_NOEXCEPT : allocator{ std::move(other.allocator) }, bucket_allocator{ std::move(other.bucket_allocator) }, sparse{ std::exchange(other.sparse, bucket_alloc_pointer{}) }, packed{ std::exchange(other.packed, alloc_pointer{}) }, bucket{ std::exchange(other.bucket, 0u) }, count{ std::exchange(other.count, 0u) }, reserved{ std::exchange(other.reserved, 0u) }, free_list{ std::exchange(other.free_list, tombstone) }, mode{ other.mode } {} /*! @brief Default destructor. */ virtual ~basic_sparse_set() { release_memory(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_sparse_set& operator=(basic_sparse_set&& other) ENTT_NOEXCEPT { release_memory(); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); sparse = std::exchange(other.sparse, bucket_alloc_pointer{}); packed = std::exchange(other.packed, alloc_pointer{}); bucket = std::exchange(other.bucket, 0u); count = std::exchange(other.count, 0u); reserved = std::exchange(other.reserved, 0u); free_list = std::exchange(other.free_list, tombstone); mode = other.mode; return *this; } /** * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ [[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { return mode; } /** * @brief Returns the next slot available for insertion. * @return The next slot available for insertion. */ [[nodiscard]] size_type slot() const ENTT_NOEXCEPT { return free_list == null ? count : size_type{ traits_type::to_entity(free_list) }; } /** * @brief Increases the capacity of a sparse set. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) { if (cap > reserved) { resize_packed(cap); } } /** * @brief Returns the number of elements that a sparse set has currently * allocated space for. * @return Capacity of the sparse set. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return reserved; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if (count < reserved) { resize_packed(count); } } /** * @brief Returns the extent of a sparse set. * * The extent of a sparse set is also the size of the internal sparse array. * There is no guarantee that the internal packed array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const ENTT_NOEXCEPT { return bucket * sparse_page; } /** * @brief Returns the number of elements in a sparse set. * * The number of elements is also the size of the internal packed array. * There is no guarantee that the internal sparse array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Number of elements. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return count; } /** * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return (count == size_type{}); } /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ [[nodiscard]] pointer data() const ENTT_NOEXCEPT { return packed; } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first entity of the internal packed * array. If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the internal packed array. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ std::addressof(packed), static_cast(count) }; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last entity in * the internal packed array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last entity of the * internal packed array. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{ std::addressof(packed), {} }; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first entity of the reversed internal * packed array. If the sparse set is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last entity in * the reversed internal packed array. Attempting to dereference the * returned iterator results in undefined behavior. * * @return An iterator to the element following the last entity of the * reversed internal packed array. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return std::make_reverse_iterator(begin()); } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { return contains(entt) ? --(end() - index(entt)) : end(); } /** * @brief Checks if a sparse set contains an entity. * @param entt A valid entity identifier. * @return True if the sparse set contains the entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(entt != tombstone && entt != null, "Invalid entity"); const auto curr = page(entt); // testing versions permits to avoid accessing the packed array return (curr < bucket&& sparse[curr] && sparse[curr][offset(entt)] != null); } /** * @brief Returns the position of an entity in a sparse set. * * @warning * Attempting to get the position of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid entity identifier. * @return The position of the entity in the sparse set. */ [[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt), "Set does not contain entity"); return size_type{ traits_type::to_entity(sparse[page(entt)][offset(entt)]) }; } /** * @brief Returns the entity at specified location, with bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ [[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { return pos < count ? packed[pos] : null; } /** * @brief Returns the entity at specified location, without bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location. */ [[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { ENTT_ASSERT(pos < count, "Position is out of bounds"); return packed[pos]; } /** * @brief Appends an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @return The slot used for insertion. */ size_type emplace_back(const entity_type entt) { ENTT_ASSERT(!contains(entt), "Set already contains entity"); if (count == reserved) { const size_type sz = static_cast(reserved * growth_factor); resize_packed(sz + !(sz > reserved)); } assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast(count)); packed[count] = entt; return count++; } /** * @brief Assigns an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @return The slot used for insertion. */ size_type emplace(const entity_type entt) { if (free_list == null) { return emplace_back(entt); } else { ENTT_ASSERT(!contains(entt), "Set already contains entity"); const auto pos = size_type{ traits_type::to_entity(free_list) }; sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast(pos)); free_list = std::exchange(packed[pos], entt); return pos; } } /** * @brief Assigns one or more entities to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last) { reserve(count + std::distance(first, last)); for (; first != last; ++first) { const auto entt = *first; ENTT_ASSERT(!contains(entt), "Set already contains entity"); assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast(count)); packed[count++] = entt; } } /** * @brief Erases an entity from a sparse set. * * @warning * Attempting to erase an entity that doesn't belong to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ void erase(const entity_type entt, void* ud = nullptr) { ENTT_ASSERT(contains(entt), "Set does not contain entity"); (mode == deletion_policy::in_place) ? in_place_pop(entt, ud) : swap_and_pop(entt, ud); } /** * @brief Erases entities from a set. * * @sa erase * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param ud Optional user data that are forwarded as-is to derived classes. */ template void erase(It first, It last, void* ud = nullptr) { for (; first != last; ++first) { erase(*first, ud); } } /** * @brief Removes an entity from a sparse set if it exists. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. * @return True if the entity is actually removed, false otherwise. */ bool remove(const entity_type entt, void* ud = nullptr) { return contains(entt) && (erase(entt, ud), true); } /** * @brief Removes entities from a sparse set if they exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param ud Optional user data that are forwarded as-is to derived classes. * @return The number of entities actually removed. */ template size_type remove(It first, It last, void* ud = nullptr) { size_type found{}; for (; first != last; ++first) { found += remove(*first, ud); } return found; } /*! @brief Removes all tombstones from the packed array of a sparse set. */ void compact() { size_type next = count; for (; next && packed[next - 1u] == tombstone; --next); for (auto* it = &free_list; *it != null && next; it = std::addressof(packed[traits_type::to_entity(*it)])) { if (const size_type pos = traits_type::to_entity(*it); pos < next) { --next; move_and_pop(next, pos); std::swap(packed[next], packed[pos]); sparse[page(packed[pos])][offset(packed[pos])] = traits_type::construct(static_cast(pos)); *it = traits_type::construct(static_cast(next)); for (; next && packed[next - 1u] == tombstone; --next); } } free_list = tombstone; count = next; } /** * @copybrief swap_at * * For what it's worth, this function affects both the internal sparse array * and the internal packed array. Users should not care of that anyway. * * @warning * Attempting to swap entities that don't belong to the sparse set results * in undefined behavior. * * @param lhs A valid entity identifier. * @param rhs A valid entity identifier. */ void swap(const entity_type lhs, const entity_type rhs) { ENTT_ASSERT(contains(lhs), "Set does not contain entity"); ENTT_ASSERT(contains(rhs), "Set does not contain entity"); auto& entt = sparse[page(lhs)][offset(lhs)]; auto& other = sparse[page(rhs)][offset(rhs)]; const auto from = size_type{ traits_type::to_entity(entt) }; const auto to = size_type{ traits_type::to_entity(other) }; // basic no-leak guarantee (with invalid state) if swapping throws swap_at(from, to); std::swap(entt, other); std::swap(packed[from], packed[to]); } /** * @brief Sort the first count elements according to the given comparison * function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to the following: * * @code{.cpp} * bool(const Entity, const Entity); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) { // basic no-leak guarantee (with invalid state) if sorting throws ENTT_ASSERT(!(length > count), "Length exceeds the number of elements"); compact(); algo(std::make_reverse_iterator(packed + length), std::make_reverse_iterator(packed), std::move(compare), std::forward(args)...); for (size_type pos{}; pos < length; ++pos) { auto curr = pos; auto next = index(packed[curr]); while (curr != next) { const auto idx = index(packed[next]); const auto entt = packed[curr]; swap_at(next, idx); sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast(curr)); curr = std::exchange(next, idx); } } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { sort_n(count, std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally * according to the order they have in `other`. All the other entities goes * to the end of the list and there are no guarantees on their order.
* In other terms, this function can be used to impose the same order on two * sets by using one of them as a master and the other one as a slave. * * Iterating the sparse set with a couple of iterators returns elements in * the expected order after a call to `respect`. See `begin` and `end` for * more details. * * @param other The sparse sets that imposes the order of the entities. */ void respect(const basic_sparse_set& other) { compact(); const auto to = other.end(); auto from = other.begin(); for (size_type pos = count - 1; pos && from != to; ++from) { if (contains(*from)) { if (*from != packed[pos]) { // basic no-leak guarantee (with invalid state) if swapping throws swap(packed[pos], *from); } --pos; } } } /** * @brief Clears a sparse set. * @param ud Optional user data that are forwarded as-is to derived classes. */ void clear(void* ud = nullptr) { for (auto&& entity : *this) { if (entity != tombstone) { in_place_pop(entity, ud); } } free_list = tombstone; count = 0u; } private: typename alloc_traits::allocator_type allocator; typename bucket_alloc_traits::allocator_type bucket_allocator; bucket_alloc_pointer sparse; alloc_pointer packed; std::size_t bucket; std::size_t count; std::size_t reserved; entity_type free_list; deletion_policy mode; }; } #endif // #include "storage.hpp" #ifndef ENTT_ENTITY_STORAGE_HPP #define ENTT_ENTITY_STORAGE_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template auto function_pointer(Ret(*)(Args...))->Ret(*)(Args...); template auto function_pointer(Ret(*)(Type, Args...), Other&&)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...), Other &&...)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...) const, Other &&...)->Ret(*)(Args...); template auto function_pointer(Type Class::*, Other &&...)->Type(*)(); template using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { return std::index_sequence_for{}; } } /** * Internal details not to be documented. * @endcond */ /*! @brief Used to wrap a function or a member of a specified type. */ template struct connect_arg_t {}; /*! @brief Constant of type connect_arg_t used to disambiguate calls. */ template inline constexpr connect_arg_t connect_arg{}; /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void*, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type&, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type*, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void*, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() ENTT_NOEXCEPT : fn{ nullptr }, data{ nullptr } {} /** * @brief Constructs a delegate and connects a free function or an unbound * member. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ENTT_NOEXCEPT { connect(); } /** * @brief Constructs a delegate and connects a free function with payload or * a bound member. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template delegate(connect_arg_t, Type&& value_or_instance) ENTT_NOEXCEPT { connect(std::forward(value_or_instance)); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() ENTT_NOEXCEPT { data = nullptr; if constexpr (std::is_invocable_r_v) { fn = [](const void*, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr (std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type& value_or_instance) ENTT_NOEXCEPT { data = &value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type* value_or_instance) ENTT_NOEXCEPT { data = value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { fn = function; data = payload; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() ENTT_NOEXCEPT { fn = nullptr; data = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void* instance() const ENTT_NOEXCEPT { return data; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(data, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { // no need to test also data return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate& other) const ENTT_NOEXCEPT { return fn == other.fn && data == other.data; } private: function_type* fn; const void* data; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate& lhs, const delegate& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ->delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type&&) ->delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret(*)(const void*, Args...), const void* = nullptr) ->delegate; } #endif // #include "fwd.hpp" #ifndef ENTT_SIGNAL_FWD_HPP #define ENTT_SIGNAL_FWD_HPP namespace entt { template class delegate; class dispatcher; template class emitter; class connection; struct scoped_connection; template class sink; template class sigh; } #endif namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink; /** * @brief Instance type when it comes to connecting member functions. * @tparam Class Type of class to which the member function belongs. */ template using instance_type = Class*; /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for (auto&& call : std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for (auto&& call : calls) { if constexpr (std::is_void_v) { if constexpr (std::is_invocable_r_v) { call(args...); if (func()) { break; } } else { call(args...); func(); } } else { if constexpr (std::is_invocable_r_v) { if (func(call(args...))) { break; } } else { func(call(args...)); } } } } private: std::vector> calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void* ref) : disconnect{ fn }, signal{ ref } {} public: /*! @brief Default constructor. */ connection() = default; /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if (disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void* signal{}; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection& other) : conn{ other } {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection&) = delete; /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection& operator=(const scoped_connection&) = delete; /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection& operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sink { using signal_type = sigh; using difference_type = typename std::iterator_traits::difference_type; template static void release(Type value_or_instance, void* signal) { sink{ *static_cast(signal) }.disconnect(value_or_instance); } template static void release(void* signal) { sink{ *static_cast(signal) }.disconnect(); } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh& ref) ENTT_NOEXCEPT : offset{}, signal{ &ref } {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type&& value_or_instance) { delegate call{}; call.template connect(value_or_instance); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type& value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type* value_or_instance) { sink other{ *this }; if (value_or_instance) { const auto& calls = signal->calls; const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }); other.offset = std::distance(it, calls.cend()); } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{ *this }; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function or an unbound member to a signal. * * The signal handler performs checks to avoid multiple connections for the * same function. * * @tparam Candidate Function or member to connect to the signal. * @return A properly initialized connection object. */ template connection connect() { disconnect(); delegate call{}; call.template connect(); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(); return { std::move(conn), signal }; } /** * @brief Connects a free function with payload or a bound member to a * signal. * * The signal isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized connection object. */ template connection connect(Type&& value_or_instance) { disconnect(value_or_instance); delegate call{}; call.template connect(value_or_instance); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance); return { std::move(conn), signal }; } /** * @brief Disconnects a free function or an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. */ template void disconnect() { auto& calls = signal->calls; delegate call{}; call.template connect(); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects a free function with payload or a bound member from a * signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type&& value_or_instance) { auto& calls = signal->calls; delegate call{}; call.template connect(value_or_instance); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type& value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type* value_or_instance) { if (value_or_instance) { auto& calls = signal->calls; calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type* signal; }; /** * @brief Deduction guide. * * It allows to deduce the function type of a sink directly from the signal it * refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template sink(sigh&) ->sink; } #endif // #include "component.hpp" #ifndef ENTT_ENTITY_COMPONENT_HPP #define ENTT_ENTITY_COMPONENT_HPP #include // #include "../config/config.h" namespace entt { /*! @brief Commonly used default traits for all types. */ struct basic_component_traits { /*! @brief Pointer stability, default is `std::false_type`. */ using in_place_delete = std::false_type; /*! @brief Empty type optimization, default is `ENTT_IGNORE_IF_EMPTY`. */ using ignore_if_empty = ENTT_IGNORE_IF_EMPTY; }; /** * @brief Common way to access various properties of components. * @tparam Type Type of component. */ template struct component_traits : basic_component_traits { static_assert(std::is_same_v, Type>, "Unsupported type"); }; } #endif // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" namespace entt { /** * @brief Basic storage implementation. * * This class is a refinement of a sparse set that associates an object to an * entity. The main purpose of this class is to extend sparse sets to store * components in a registry. It guarantees fast access both to the elements and * to the entities. * * @note * Entities and objects have the same order. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that objects are returned in the insertion order when iterate * a storage. Do not make assumption on the order in any case. * * @warning * Empty types aren't explicitly instantiated. Therefore, many of the functions * normally available for non-empty types will not be available for empty ones. * * @sa sparse_set * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_storage_impl : public basic_sparse_set::template rebind_alloc> { static constexpr auto packed_page = ENTT_PACKED_PAGE; using comp_traits = component_traits; using underlying_type = basic_sparse_set::template rebind_alloc>; using difference_type = typename entt_traits::difference_type; using alloc_traits = typename std::allocator_traits::template rebind_traits; using alloc_pointer = typename alloc_traits::pointer; using alloc_const_pointer = typename alloc_traits::const_pointer; using bucket_alloc_traits = typename std::allocator_traits::template rebind_traits; using bucket_alloc_pointer = typename bucket_alloc_traits::pointer; using bucket_alloc_const_type = typename std::allocator_traits::template rebind_alloc; using bucket_alloc_const_pointer = typename std::allocator_traits::const_pointer; static_assert(alloc_traits::propagate_on_container_move_assignment::value); static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value); template struct storage_iterator final { using difference_type = typename basic_storage_impl::difference_type; using value_type = Value; using pointer = value_type*; using reference = value_type&; using iterator_category = std::random_access_iterator_tag; storage_iterator() ENTT_NOEXCEPT = default; storage_iterator(bucket_alloc_pointer const* ref, const typename basic_storage_impl::difference_type idx) ENTT_NOEXCEPT : packed{ ref }, index{ idx } {} storage_iterator& operator++() ENTT_NOEXCEPT { return --index, * this; } storage_iterator operator++(int) ENTT_NOEXCEPT { storage_iterator orig = *this; return ++(*this), orig; } storage_iterator& operator--() ENTT_NOEXCEPT { return ++index, * this; } storage_iterator operator--(int) ENTT_NOEXCEPT { storage_iterator orig = *this; return operator--(), orig; } storage_iterator& operator+=(const difference_type value) ENTT_NOEXCEPT { index -= value; return *this; } storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { storage_iterator copy = *this; return (copy += value); } storage_iterator& operator-=(const difference_type value) ENTT_NOEXCEPT { return (*this += -value); } storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { return (*this + -value); } difference_type operator-(const storage_iterator& other) const ENTT_NOEXCEPT { return other.index - index; } [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { const auto pos = size_type(index - value - 1); return (*packed)[page(pos)][offset(pos)]; } [[nodiscard]] bool operator==(const storage_iterator& other) const ENTT_NOEXCEPT { return other.index == index; } [[nodiscard]] bool operator!=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] bool operator<(const storage_iterator& other) const ENTT_NOEXCEPT { return index > other.index; } [[nodiscard]] bool operator>(const storage_iterator& other) const ENTT_NOEXCEPT { return index < other.index; } [[nodiscard]] bool operator<=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this > other); } [[nodiscard]] bool operator>=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this < other); } [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { const auto pos = size_type(index - 1u); return std::addressof((*packed)[page(pos)][offset(pos)]); } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return *operator->(); } private: bucket_alloc_pointer const* packed; difference_type index; }; [[nodiscard]] static auto page(const std::size_t pos) ENTT_NOEXCEPT { return pos / packed_page; } [[nodiscard]] static auto offset(const std::size_t pos) ENTT_NOEXCEPT { return pos & (packed_page - 1); } void release_memory() { if (packed) { // no-throw stable erase iteration underlying_type::clear(); for (size_type pos{}; pos < bucket; ++pos) { alloc_traits::deallocate(allocator, packed[pos], packed_page); bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos])); } bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); } } void assure_at_least(const std::size_t last) { if (const auto idx = page(last - 1u); !(idx < bucket)) { const size_type sz = idx + 1u; const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz); std::uninitialized_copy(packed, packed + bucket, mem); size_type pos{}; ENTT_TRY{ for (pos = bucket; pos < sz; ++pos) { auto pg = alloc_traits::allocate(allocator, packed_page); bucket_alloc_traits::construct(bucket_allocator, std::addressof(mem[pos]), pg); } } ENTT_CATCH{ for (auto next = bucket; next < pos; ++next) { alloc_traits::deallocate(allocator, mem[next], packed_page); } std::destroy(mem, mem + pos); bucket_alloc_traits::deallocate(bucket_allocator, mem, sz); ENTT_THROW; } std::destroy(packed, packed + bucket); bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); packed = mem; bucket = sz; } } void release_unused_pages() { if (const auto length = underlying_type::size() / packed_page; length < bucket) { const auto mem = bucket_alloc_traits::allocate(bucket_allocator, length); std::uninitialized_copy(packed, packed + length, mem); for (auto pos = length; pos < bucket; ++pos) { alloc_traits::deallocate(allocator, packed[pos], packed_page); bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos])); } bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); packed = mem; bucket = length; } } template auto& push_at(const std::size_t pos, Args &&... args) { ENTT_ASSERT(pos < (bucket* packed_page), "Out of bounds index"); auto* instance = std::addressof(packed[page(pos)][offset(pos)]); if constexpr (std::is_aggregate_v) { alloc_traits::construct(allocator, instance, Type{ std::forward(args)... }); } else { alloc_traits::construct(allocator, instance, std::forward(args)...); } return *instance; } void pop_at(const std::size_t pos) { alloc_traits::destroy(allocator, std::addressof(packed[page(pos)][offset(pos)])); } protected: /*! @copydoc basic_sparse_set::swap_at */ void swap_at(const std::size_t lhs, const std::size_t rhs) final { std::swap(packed[page(lhs)][offset(lhs)], packed[page(rhs)][offset(rhs)]); } /*! @copydoc basic_sparse_set::move_and_pop */ void move_and_pop(const std::size_t from, const std::size_t to) final { push_at(to, std::move(packed[page(from)][offset(from)])); pop_at(from); } /*! @copydoc basic_sparse_set::swap_and_pop */ void swap_and_pop(const Entity entt, void* ud) override { const auto pos = underlying_type::index(entt); const auto last = underlying_type::size() - 1u; auto&& elem = packed[page(pos)][offset(pos)]; // support for nosy destructors [[maybe_unused]] auto unused = std::move(elem); elem = std::move(packed[page(last)][offset(last)]); pop_at(last); underlying_type::swap_and_pop(entt, ud); } /*! @copydoc basic_sparse_set::in_place_pop */ void in_place_pop(const Entity entt, void* ud) override { const auto pos = underlying_type::index(entt); underlying_type::in_place_pop(entt, ud); // support for nosy destructors pop_at(pos); } public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained elements. */ using pointer = bucket_alloc_pointer; /*! @brief Constant pointer type to contained elements. */ using const_pointer = bucket_alloc_const_pointer; /*! @brief Random access iterator type. */ using iterator = storage_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = std::reverse_iterator; /** * @brief Default constructor. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_storage_impl(const allocator_type& alloc = {}) : underlying_type{ deletion_policy{comp_traits::in_place_delete::value}, alloc }, allocator{ alloc }, bucket_allocator{ alloc }, packed{ bucket_alloc_traits::allocate(bucket_allocator, 0u) }, bucket{} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage_impl(basic_storage_impl&& other) ENTT_NOEXCEPT : underlying_type{ std::move(other) }, allocator{ std::move(other.allocator) }, bucket_allocator{ std::move(other.bucket_allocator) }, packed{ std::exchange(other.packed, bucket_alloc_pointer{}) }, bucket{ std::exchange(other.bucket, 0u) } {} /*! @brief Default destructor. */ ~basic_storage_impl() override { release_memory(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_storage_impl& operator=(basic_storage_impl&& other) ENTT_NOEXCEPT { release_memory(); underlying_type::operator=(std::move(other)); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); packed = std::exchange(other.packed, bucket_alloc_pointer{}); bucket = std::exchange(other.bucket, 0u); return *this; } /** * @brief Increases the capacity of a storage. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) { underlying_type::reserve(cap); if (cap > underlying_type::size()) { assure_at_least(cap); } } /** * @brief Returns the number of elements that a storage has currently * allocated space for. * @return Capacity of the storage. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return bucket * packed_page; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { underlying_type::shrink_to_fit(); release_unused_pages(); } /** * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { return packed; } /*! @copydoc raw */ [[nodiscard]] pointer raw() ENTT_NOEXCEPT { return packed; } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the storage is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { const difference_type pos = underlying_type::size(); return const_iterator{ std::addressof(packed), pos }; } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() ENTT_NOEXCEPT { const difference_type pos = underlying_type::size(); return iterator{ std::addressof(packed), pos }; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { return const_iterator{ std::addressof(packed), {} }; } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() ENTT_NOEXCEPT { return iterator{ std::addressof(packed), {} }; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first instance of the reversed * internal array. If the storage is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first instance of the reversed internal array. */ [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { return crbegin(); } /*! @copydoc rbegin */ [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last instance * of the reversed internal array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last instance of the * reversed internal array. */ [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { return crend(); } /*! @copydoc rend */ [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { return std::make_reverse_iterator(begin()); } /** * @brief Returns the object assigned to an entity. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid entity identifier. * @return The object assigned to the entity. */ [[nodiscard]] const value_type& get(const entity_type entt) const ENTT_NOEXCEPT { const auto idx = underlying_type::index(entt); return packed[page(idx)][offset(idx)]; } /*! @copydoc get */ [[nodiscard]] value_type& get(const entity_type entt) ENTT_NOEXCEPT { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Assigns an entity to a storage and constructs its object. * * This version accept both types that can be constructed in place directly * and types like aggregates that do not work well with a placement new as * performed usually under the hood during an _emplace back_. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to construct an object for the entity. * @return A reference to the newly created object. */ template value_type& emplace(const entity_type entt, Args &&... args) { const auto pos = underlying_type::slot(); assure_at_least(pos + 1u); auto& value = push_at(pos, std::forward(args)...); ENTT_TRY{ [[maybe_unused]] const auto curr = underlying_type::emplace(entt); ENTT_ASSERT(pos == curr, "Misplaced component"); } ENTT_CATCH{ pop_at(pos); ENTT_THROW; } return value; } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the updated instance. */ template decltype(auto) patch(const entity_type entt, Func &&... func) { const auto idx = underlying_type::index(entt); auto&& elem = packed[page(idx)][offset(idx)]; (std::forward(func)(elem), ...); return elem; } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given instance. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the object to construct. */ template void insert(It first, It last, const value_type& value = {}) { const auto cap = underlying_type::size() + std::distance(first, last); underlying_type::reserve(cap); assure_at_least(cap); for (; first != last; ++first) { push_at(underlying_type::size(), value); ENTT_TRY{ underlying_type::emplace_back(*first); } ENTT_CATCH{ pop_at(underlying_type::size()); ENTT_THROW; } } } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given range. * * @sa construct * * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of objects. */ template::value_type>, value_type>>> void insert(EIt first, EIt last, CIt from) { const auto cap = underlying_type::size() + std::distance(first, last); underlying_type::reserve(cap); assure_at_least(cap); for (; first != last; ++first, ++from) { push_at(underlying_type::size(), *from); ENTT_TRY{ underlying_type::emplace_back(*first); } ENTT_CATCH{ pop_at(underlying_type::size()); ENTT_THROW; } } } /** * @brief Sort elements according to the given comparison function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(const Entity, const Entity); * bool(const Type &, const Type &); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @warning * Empty types are never instantiated. Therefore, only comparison function * objects that require to return entities rather than components are * accepted. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) { if constexpr (std::is_invocable_v) { underlying_type::sort_n(length, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { const auto ilhs = underlying_type::index(lhs), irhs = underlying_type::index(rhs); return compare(std::as_const(packed[page(ilhs)][offset(ilhs)]), std::as_const(packed[page(irhs)][offset(irhs)])); }, std::move(algo), std::forward(args)...); } else { underlying_type::sort_n(length, std::move(compare), std::move(algo), std::forward(args)...); } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { sort_n(underlying_type::size(), std::move(compare), std::move(algo), std::forward(args)...); } private: typename alloc_traits::allocator_type allocator; typename bucket_alloc_traits::allocator_type bucket_allocator; bucket_alloc_pointer packed; size_type bucket; }; /*! @copydoc basic_storage_impl */ template class basic_storage_impl::ignore_if_empty::value&& std::is_empty_v>> : public basic_sparse_set::template rebind_alloc> { using comp_traits = component_traits; using underlying_type = basic_sparse_set::template rebind_alloc>; using alloc_traits = typename std::allocator_traits::template rebind_traits; public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /** * @brief Default constructor. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_storage_impl(const allocator_type& alloc = {}) : underlying_type{ deletion_policy{comp_traits::in_place_delete::value}, alloc } {} /** * @brief Fake get function. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid entity identifier. */ void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(underlying_type::contains(entt), "Storage does not contain entity"); } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to construct an object for the entity. */ template void emplace(const entity_type entt, Args &&... args) { [[maybe_unused]] value_type instance{ std::forward(args)... }; underlying_type::emplace(entt); } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. */ template void patch([[maybe_unused]] const entity_type entt, Func &&... func) { ENTT_ASSERT(underlying_type::contains(entt), "Storage does not contain entity"); (std::forward(func)(), ...); } /** * @brief Assigns one or more entities to a storage. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last, const value_type & = {}) { underlying_type::insert(first, last); } }; /** * @brief Mixin type to use to wrap basic storage classes. * @tparam Type The type of the underlying storage. */ template struct storage_adapter_mixin : Type { static_assert(std::is_same_v>, "Invalid object type"); /*! @brief Type of the objects assigned to entities. */ using value_type = typename Type::value_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Inherited constructors. */ using Type::Type; /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(basic_registry&, const entity_type entt, Args &&... args) { return Type::emplace(entt, std::forward(args)...); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(basic_registry&, It first, It last, Args &&... args) { Type::insert(first, last, std::forward(args)...); } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(basic_registry&, const entity_type entt, Func &&... func) { return Type::patch(entt, std::forward(func)...); } }; /** * @brief Mixin type to use to add signal support to storage types. * @tparam Type The type of the underlying storage. */ template class sigh_storage_mixin final : public Type { /*! @copydoc basic_sparse_set::swap_and_pop */ void swap_and_pop(const typename Type::entity_type entt, void* ud) final { ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry"); destruction.publish(*static_cast *>(ud), entt); Type::swap_and_pop(entt, ud); } /*! @copydoc basic_sparse_set::in_place_pop */ void in_place_pop(const typename Type::entity_type entt, void* ud) final { ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry"); destruction.publish(*static_cast *>(ud), entt); Type::in_place_pop(entt, ud); } public: /*! @brief Underlying value type. */ using value_type = typename Type::value_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Inherited constructors. */ using Type::Type; /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever a new instance is created and assigned to an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **after** the object has been assigned to the * entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { return sink{ construction }; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **after** the object has been updated. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_update() ENTT_NOEXCEPT { return sink{ update }; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is removed from an entity and thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **before** the object has been removed from the * entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { return sink{ destruction }; } /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param owner The registry that issued the request. * @param entt A valid entity identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(basic_registry& owner, const entity_type entt, Args &&... args) { Type::emplace(entt, std::forward(args)...); construction.publish(owner, entt); return this->get(entt); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param owner The registry that issued the request. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(basic_registry& owner, It first, It last, Args &&... args) { Type::insert(first, last, std::forward(args)...); if (!construction.empty()) { for (; first != last; ++first) { construction.publish(owner, *first); } } } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param owner The registry that issued the request. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(basic_registry& owner, const entity_type entt, Func &&... func) { Type::patch(entt, std::forward(func)...); update.publish(owner, entt); return this->get(entt); } private: sigh&, const entity_type)> construction{}; sigh&, const entity_type)> destruction{}; sigh&, const entity_type)> update{}; }; /** * @brief Storage implementation dispatcher. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct basic_storage : basic_storage_impl { using basic_storage_impl::basic_storage_impl; }; /** * @brief Provides a common way to access certain properties of storage types. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects managed by the storage class. */ template struct storage_traits { /*! @brief Resulting type after component-to-storage conversion. */ using storage_type = sigh_storage_mixin>; }; /** * @brief Gets the element assigned to an entity from a storage, if any. * @tparam Type Storage type. * @param container A valid instance of a storage class. * @param entt A valid entity identifier. * @return A possibly empty tuple containing the requested element. */ template [[nodiscard]] auto get_as_tuple([[maybe_unused]] Type& container, [[maybe_unused]] const typename Type::entity_type entt) { static_assert(std::is_same_v, typename storage_traits::storage_type>, "Invalid storage"); if constexpr (std::is_void_v ) { return std::make_tuple(); } else { return std::forward_as_tuple(container.get(entt)); } } } #endif // #include "utility.hpp" #ifndef ENTT_ENTITY_UTILITY_HPP #define ENTT_ENTITY_UTILITY_HPP // #include "../core/type_traits.hpp" namespace entt { /** * @brief Alias for exclusion lists. * @tparam Type List of types. */ template struct exclude_t : type_list {}; /** * @brief Variable template for exclusion lists. * @tparam Type List of types. */ template inline constexpr exclude_t exclude{}; /** * @brief Alias for lists of observed components. * @tparam Type List of types. */ template struct get_t : type_list {}; /** * @brief Variable template for lists of observed components. * @tparam Type List of types. */ template inline constexpr get_t get{}; } #endif namespace entt { /** * @brief Group. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_group; /** * @brief Non-owning group. * * A non-owning group returns all entities and only the entities that have at * least the given components. Moreover, it's guaranteed that the entity list * is tightly packed in memory for fast iterations. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Groups share references to the underlying data structures of the registry * that generated them. Therefore any change to the entities and to the * components made by means of the registry are immediately reflected by all the * groups.
* Moreover, sorting a non-owning group affects all the instances of the same * group (it means that users don't have to call `sort` on each instance to sort * all of them because they _share_ entities and components). * * @warning * Lifetime of a group must not overcome that of the registry that generated it. * In any other case, attempting to use a group results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the group. * @tparam Get Type of components observed by the group. */ template class basic_group, get_t> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It from, const std::tuple *...>& args) ENTT_NOEXCEPT : it{ from }, pools{ args } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { const auto entt = *it; return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get*>(pools), entt)...); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; std::tuple *...> pools; }; public: using iterator = iterable_iterator; using reverse_iterator = iterable_iterator; iterable(basic_common_type* const ref, const std::tuple *...>& cpools) : handler{ ref }, pools{ cpools } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return handler ? iterator{ handler->begin(), pools } : iterator{ {}, pools }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return handler ? iterator{ handler->end(), pools } : iterator{ {}, pools }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return handler ? reverse_iterator{ handler->rbegin(), pools } : reverse_iterator{ {}, pools }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return handler ? reverse_iterator{ handler->rend(), pools } : reverse_iterator{ {}, pools }; } private: basic_common_type* const handler; const std::tuple *...> pools; }; basic_group(basic_common_type& ref, storage_type &... gpool) ENTT_NOEXCEPT : handler{ &ref }, pools{ &gpool... } {} public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable_group = iterable; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() ENTT_NOEXCEPT : handler{} {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return *this ? handler->size() : size_type{}; } /** * @brief Returns the number of elements that a group has currently * allocated space for. * @return Capacity of the group. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return *this ? handler->capacity() : size_type{}; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if (*this) { handler->shrink_to_fit(); } } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return !*this || handler->empty(); } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return *this ? handler->data() : nullptr; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return *this ? handler->begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return *this ? handler->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return *this ? handler->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return *this ? handler->rend() : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return handler != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return *this && handler->contains(entt); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "Group does not contain entity"); if constexpr (sizeof...(Component) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Component) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (const auto entt : *this) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } /** * @brief Returns an iterable object to use to _visit_ the group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { return iterable_group{ handler, pools }; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Component &..., const Component &...); * bool(const Entity, const Entity); * @endcode * * Where `Component` are such that they are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Component Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { if (*this) { if constexpr (sizeof...(Component) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); handler->sort(std::move(compare), std::move(algo), std::forward(args)...); } else if constexpr (sizeof...(Component) == 1) { handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare((std::get*>(pools)->get(lhs), ...), (std::get*>(pools)->get(rhs), ...)); }, std::move(algo), std::forward(args)...); } else { handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare(std::forward_as_tuple(std::get*>(pools)->get(lhs)...), std::forward_as_tuple(std::get*>(pools)->get(rhs)...)); }, std::move(algo), std::forward(args)...); } } } /** * @brief Sort the shared pool of entities according to the given component. * * Non-owning groups of the same type share with the registry a pool of * entities with its own order that doesn't depend on the order of any pool * of components. Users can order the underlying data structure so that it * respects the order of the pool of the given component. * * @note * The shared pool of entities and thus its order is affected by the changes * to each and every pool that it tracks. Therefore changes to those pools * can quickly ruin the order imposed to the pool of entities shared between * the non-owning groups. * * @tparam Component Type of component to use to impose the order. */ template void sort() const { if (*this) { handler->respect(*std::get*>(pools)); } } private: basic_common_type* const handler; const std::tuple *...> pools; }; /** * @brief Owning group. * * Owning groups return all entities and only the entities that have at least * the given components. Moreover: * * * It's guaranteed that the entity list is tightly packed in memory for fast * iterations. * * It's guaranteed that the lists of owned components are tightly packed in * memory for even faster iterations and to allow direct access. * * They stay true to the order of the owned components and all instances have * the same order in memory. * * The more types of components are owned by a group, the faster it is to * iterate them. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Groups share references to the underlying data structures of the registry * that generated them. Therefore any change to the entities and to the * components made by means of the registry are immediately reflected by all the * groups. * Moreover, sorting an owning group affects all the instance of the same group * (it means that users don't have to call `sort` on each instance to sort all * of them because they share the underlying data structure). * * @warning * Lifetime of a group must not overcome that of the registry that generated it. * In any other case, attempting to use a group results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the group. * @tparam Get Types of components observed by the group. * @tparam Owned Types of components owned by the group. */ template class basic_group, get_t, Owned...> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator; template struct iterable_iterator> final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It from, const std::tuple& other, const std::tuple *...>& cpools) ENTT_NOEXCEPT : it{ from }, owned{ std::get(other)... }, get{ cpools } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, (++std::get(owned), ...), * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat( std::make_tuple(*it), std::forward_as_tuple(*std::get(owned)...), get_as_tuple(*std::get*>(get), *it)... ); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; std::tuple owned; std::tuple *...> get; }; public: using iterator = iterable_iterator< typename basic_common_type::iterator, type_list_cat_t < std::conditional_t < std::is_void_v>().get({})) > , type_list<>, type_list>().end()) >> ... > > ; using reverse_iterator = iterable_iterator< typename basic_common_type::reverse_iterator, type_list_cat_t < std::conditional_t < std::is_void_v>().get({})) > , type_list<>, type_list>().rbegin()) >> ... > > ; iterable(std::tuple *..., storage_type *...> cpools, const std::size_t* const extent) : pools{ cpools }, length{ extent } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return length ? iterator{ std::get<0>(pools)->basic_common_type::end() - *length, std::make_tuple((std::get*>(pools)->end() - *length)...), std::make_tuple(std::get*>(pools)...) } : iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->end()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return length ? iterator{ std::get<0>(pools)->basic_common_type::end(), std::make_tuple((std::get*>(pools)->end())...), std::make_tuple(std::get*>(pools)...) } : iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->end()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return length ? reverse_iterator{ std::get<0>(pools)->basic_common_type::rbegin(), std::make_tuple((std::get*>(pools)->rbegin())...), std::make_tuple(std::get*>(pools)...) } : reverse_iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->rbegin()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return length ? reverse_iterator{ std::get<0>(pools)->basic_common_type::rbegin() + *length, std::make_tuple((std::get*>(pools)->rbegin() + *length)...), std::make_tuple(std::get*>(pools)...) } : reverse_iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->rbegin()){}...), std::make_tuple(std::get*>(pools)...) }; } private: const std::tuple *..., storage_type *...> pools; const std::size_t* const length; }; basic_group(const std::size_t& extent, storage_type &... opool, storage_type &... gpool) ENTT_NOEXCEPT : pools{ &opool..., &gpool... }, length{ &extent } {} public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable_group = iterable; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() ENTT_NOEXCEPT : length{} {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return *this ? *length : size_type{}; } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return !*this || !*length; } /** * @brief Direct access to the raw representation offered by the storage. * * @warning * This function is only available for owned types. * * @tparam Component Type of component in which one is interested. * @return A pointer to the array of components. */ template [[nodiscard]] auto raw() const ENTT_NOEXCEPT { static_assert((std::is_same_v || ...), "Non-owned type"); auto* cpool = std::get*>(pools); return cpool ? cpool->raw() : decltype(cpool->raw()){}; } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->data() : nullptr; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return *this ? (std::get<0>(pools)->basic_common_type::end() - *length) : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->basic_common_type::end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->basic_common_type::rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return *this ? (std::get<0>(pools)->basic_common_type::rbegin() + *length) : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; return it != end() && it >= begin() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return length != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "Group does not contain entity"); if constexpr (sizeof...(Component) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)..., get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Component) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (auto args : each()) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, args); } else { std::apply([&func](auto, auto &&... less) { func(std::forward(less)...); }, args); } } } /** * @brief Returns an iterable object to use to _visit_ the group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { return iterable_group{ pools, length }; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Component &, const Component &); * bool(const Entity, const Entity); * @endcode * * Where `Component` are either owned types or not but still such that they * are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Component Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const { auto* cpool = std::get<0>(pools); if constexpr (sizeof...(Component) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); } else if constexpr (sizeof...(Component) == 1) { cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare((std::get*>(pools)->get(lhs), ...), (std::get*>(pools)->get(rhs), ...)); }, std::move(algo), std::forward(args)...); } else { cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare(std::forward_as_tuple(std::get*>(pools)->get(lhs)...), std::forward_as_tuple(std::get*>(pools)->get(rhs)...)); }, std::move(algo), std::forward(args)...); } [this](auto* head, auto *... other) { for (auto next = *length; next; --next) { const auto pos = next - 1; [[maybe_unused]] const auto entt = head->data()[pos]; (other->swap(other->data()[pos], entt), ...); } }(std::get*>(pools)...); } private: const std::tuple *..., storage_type *...> pools; const size_type* const length; }; } #endif // #include "entity/handle.hpp" #ifndef ENTT_ENTITY_HANDLE_HPP #define ENTT_ENTITY_HANDLE_HPP #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "fwd.hpp" // #include "registry.hpp" #ifndef ENTT_ENTITY_REGISTRY_HPP #define ENTT_ENTITY_REGISTRY_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "fwd.hpp" // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif namespace entt { /** * @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 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; using vtable_type = const void* (const operation, const basic_any&, void*); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template [[nodiscard]] static constexpr policy type_to_policy() { if constexpr (std::is_lvalue_reference_v) { if constexpr (std::is_const_v>) { return policy::CREF; } else { return policy::REF; } } else { return policy::OWNER; } } template [[nodiscard]] static bool compare(const void* lhs, const void* rhs) { if constexpr (!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); } else { return lhs == rhs; } } template 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>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { const Type* instance = (in_situ && from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); switch (op) { case operation::COPY: if constexpr (std::is_copy_constructible_v) { static_cast(to)->emplace(*instance); } break; case operation::MOVE: if constexpr (in_situ) { if (from.mode == policy::OWNER) { return new (&static_cast(to)->storage) Type{ std::move(*const_cast(instance)) }; } } return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); case operation::DTOR: if (from.mode == policy::OWNER) { if constexpr (in_situ) { instance->~Type(); } else if constexpr (std::is_array_v) { delete[] instance; } else { delete instance; } } break; case operation::COMP: return compare(instance, (*static_cast(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(to) = type_id(); break; } } return nullptr; } template void initialize([[maybe_unused]] Args &&... args) { if constexpr (!std::is_void_v) { if constexpr (std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); instance = (std::addressof(args), ...); } else if constexpr (in_situ) { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { new (&storage) Type{ std::forward(args)... }; } else { new (&storage) Type(std::forward(args)...); } } else { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { instance = new Type{ std::forward(args)... }; } else { instance = new Type(std::forward(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 }, 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 explicit basic_any(std::in_place_type_t, Args &&... args) : instance{}, vtable{ &basic_vtable>> }, mode{ type_to_policy() } { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper 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 basic_any(std::reference_wrapper value) ENTT_NOEXCEPT : basic_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, basic_any>>> basic_any(Type&& value) : instance{}, vtable{ &basic_vtable> }, mode{ policy::OWNER } { initialize>(std::forward(value)); } /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any& other) : instance{}, vtable{ &basic_vtable }, 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 [[deprecated("Use std::in_place_type, entt::make_any, emplace or forward_as_any instead")]] basic_any& operator=(std::reference_wrapper value) ENTT_NOEXCEPT { emplace(value.get()); 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 std::enable_if_t, basic_any>, basic_any&> operator=(Type&& value) { emplace>(std::forward(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(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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, *this, nullptr); mode = type_to_policy(); initialize(std::forward(args)...); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(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; }; /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ template [[nodiscard]] inline bool operator!=(const basic_any& lhs, const basic_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @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 Type any_cast(const basic_any& data) ENTT_NOEXCEPT { const auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any&& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } /*! @copydoc any_cast */ template const Type* any_cast(const basic_any* data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ template Type* any_cast(basic_any* data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } /** * @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::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&... args) { return basic_any{std::in_place_type, std::forward(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::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type&& value) { return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } } #endif // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "hashed_string.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "group.hpp" #ifndef ENTT_ENTITY_GROUP_HPP #define ENTT_ENTITY_GROUP_HPP #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" // #include "utility.hpp" namespace entt { /** * @brief Group. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_group; /** * @brief Non-owning group. * * A non-owning group returns all entities and only the entities that have at * least the given components. Moreover, it's guaranteed that the entity list * is tightly packed in memory for fast iterations. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Groups share references to the underlying data structures of the registry * that generated them. Therefore any change to the entities and to the * components made by means of the registry are immediately reflected by all the * groups.
* Moreover, sorting a non-owning group affects all the instances of the same * group (it means that users don't have to call `sort` on each instance to sort * all of them because they _share_ entities and components). * * @warning * Lifetime of a group must not overcome that of the registry that generated it. * In any other case, attempting to use a group results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the group. * @tparam Get Type of components observed by the group. */ template class basic_group, get_t> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It from, const std::tuple *...>& args) ENTT_NOEXCEPT : it{ from }, pools{ args } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { const auto entt = *it; return std::tuple_cat(std::make_tuple(entt), get_as_tuple(*std::get*>(pools), entt)...); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; std::tuple *...> pools; }; public: using iterator = iterable_iterator; using reverse_iterator = iterable_iterator; iterable(basic_common_type* const ref, const std::tuple *...>& cpools) : handler{ ref }, pools{ cpools } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return handler ? iterator{ handler->begin(), pools } : iterator{ {}, pools }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return handler ? iterator{ handler->end(), pools } : iterator{ {}, pools }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return handler ? reverse_iterator{ handler->rbegin(), pools } : reverse_iterator{ {}, pools }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return handler ? reverse_iterator{ handler->rend(), pools } : reverse_iterator{ {}, pools }; } private: basic_common_type* const handler; const std::tuple *...> pools; }; basic_group(basic_common_type& ref, storage_type &... gpool) ENTT_NOEXCEPT : handler{ &ref }, pools{ &gpool... } {} public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable_group = iterable; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() ENTT_NOEXCEPT : handler{} {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return *this ? handler->size() : size_type{}; } /** * @brief Returns the number of elements that a group has currently * allocated space for. * @return Capacity of the group. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return *this ? handler->capacity() : size_type{}; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if (*this) { handler->shrink_to_fit(); } } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return !*this || handler->empty(); } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return *this ? handler->data() : nullptr; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return *this ? handler->begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return *this ? handler->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return *this ? handler->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return *this ? handler->rend() : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return handler != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return *this && handler->contains(entt); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "Group does not contain entity"); if constexpr (sizeof...(Component) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Component) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (const auto entt : *this) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } /** * @brief Returns an iterable object to use to _visit_ the group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { return iterable_group{ handler, pools }; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Component &..., const Component &...); * bool(const Entity, const Entity); * @endcode * * Where `Component` are such that they are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Component Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { if (*this) { if constexpr (sizeof...(Component) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); handler->sort(std::move(compare), std::move(algo), std::forward(args)...); } else if constexpr (sizeof...(Component) == 1) { handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare((std::get*>(pools)->get(lhs), ...), (std::get*>(pools)->get(rhs), ...)); }, std::move(algo), std::forward(args)...); } else { handler->sort([this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare(std::forward_as_tuple(std::get*>(pools)->get(lhs)...), std::forward_as_tuple(std::get*>(pools)->get(rhs)...)); }, std::move(algo), std::forward(args)...); } } } /** * @brief Sort the shared pool of entities according to the given component. * * Non-owning groups of the same type share with the registry a pool of * entities with its own order that doesn't depend on the order of any pool * of components. Users can order the underlying data structure so that it * respects the order of the pool of the given component. * * @note * The shared pool of entities and thus its order is affected by the changes * to each and every pool that it tracks. Therefore changes to those pools * can quickly ruin the order imposed to the pool of entities shared between * the non-owning groups. * * @tparam Component Type of component to use to impose the order. */ template void sort() const { if (*this) { handler->respect(*std::get*>(pools)); } } private: basic_common_type* const handler; const std::tuple *...> pools; }; /** * @brief Owning group. * * Owning groups return all entities and only the entities that have at least * the given components. Moreover: * * * It's guaranteed that the entity list is tightly packed in memory for fast * iterations. * * It's guaranteed that the lists of owned components are tightly packed in * memory for even faster iterations and to allow direct access. * * They stay true to the order of the owned components and all instances have * the same order in memory. * * The more types of components are owned by a group, the faster it is to * iterate them. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Groups share references to the underlying data structures of the registry * that generated them. Therefore any change to the entities and to the * components made by means of the registry are immediately reflected by all the * groups. * Moreover, sorting an owning group affects all the instance of the same group * (it means that users don't have to call `sort` on each instance to sort all * of them because they share the underlying data structure). * * @warning * Lifetime of a group must not overcome that of the registry that generated it. * In any other case, attempting to use a group results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the group. * @tparam Get Types of components observed by the group. * @tparam Owned Types of components owned by the group. */ template class basic_group, get_t, Owned...> final { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator; template struct iterable_iterator> final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It from, const std::tuple& other, const std::tuple *...>& cpools) ENTT_NOEXCEPT : it{ from }, owned{ std::get(other)... }, get{ cpools } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, (++std::get(owned), ...), * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat( std::make_tuple(*it), std::forward_as_tuple(*std::get(owned)...), get_as_tuple(*std::get*>(get), *it)... ); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; std::tuple owned; std::tuple *...> get; }; public: using iterator = iterable_iterator< typename basic_common_type::iterator, type_list_cat_t < std::conditional_t < std::is_void_v>().get({})) > , type_list<>, type_list>().end()) >> ... > > ; using reverse_iterator = iterable_iterator< typename basic_common_type::reverse_iterator, type_list_cat_t < std::conditional_t < std::is_void_v>().get({})) > , type_list<>, type_list>().rbegin()) >> ... > > ; iterable(std::tuple *..., storage_type *...> cpools, const std::size_t* const extent) : pools{ cpools }, length{ extent } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return length ? iterator{ std::get<0>(pools)->basic_common_type::end() - *length, std::make_tuple((std::get*>(pools)->end() - *length)...), std::make_tuple(std::get*>(pools)...) } : iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->end()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return length ? iterator{ std::get<0>(pools)->basic_common_type::end(), std::make_tuple((std::get*>(pools)->end())...), std::make_tuple(std::get*>(pools)...) } : iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->end()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return length ? reverse_iterator{ std::get<0>(pools)->basic_common_type::rbegin(), std::make_tuple((std::get*>(pools)->rbegin())...), std::make_tuple(std::get*>(pools)...) } : reverse_iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->rbegin()){}...), std::make_tuple(std::get*>(pools)...) }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return length ? reverse_iterator{ std::get<0>(pools)->basic_common_type::rbegin() + *length, std::make_tuple((std::get*>(pools)->rbegin() + *length)...), std::make_tuple(std::get*>(pools)...) } : reverse_iterator{ {}, std::make_tuple(decltype(std::get*>(pools)->rbegin()){}...), std::make_tuple(std::get*>(pools)...) }; } private: const std::tuple *..., storage_type *...> pools; const std::size_t* const length; }; basic_group(const std::size_t& extent, storage_type &... opool, storage_type &... gpool) ENTT_NOEXCEPT : pools{ &opool..., &gpool... }, length{ &extent } {} public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable_group = iterable; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() ENTT_NOEXCEPT : length{} {} /** * @brief Returns the number of entities that have the given components. * @return Number of entities that have the given components. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return *this ? *length : size_type{}; } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return !*this || !*length; } /** * @brief Direct access to the raw representation offered by the storage. * * @warning * This function is only available for owned types. * * @tparam Component Type of component in which one is interested. * @return A pointer to the array of components. */ template [[nodiscard]] auto raw() const ENTT_NOEXCEPT { static_assert((std::is_same_v || ...), "Non-owned type"); auto* cpool = std::get*>(pools); return cpool ? cpool->raw() : decltype(cpool->raw()){}; } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->data() : nullptr; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return *this ? (std::get<0>(pools)->basic_common_type::end() - *length) : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->basic_common_type::end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return *this ? std::get<0>(pools)->basic_common_type::rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return *this ? (std::get<0>(pools)->basic_common_type::rbegin() + *length) : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; return it != end() && it >= begin() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return length != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid entity identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Component Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "Group does not contain entity"); if constexpr (sizeof...(Component) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)..., get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Component) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (auto args : each()) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, args); } else { std::apply([&func](auto, auto &&... less) { func(std::forward(less)...); }, args); } } } /** * @brief Returns an iterable object to use to _visit_ the group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable_group each() const ENTT_NOEXCEPT { return iterable_group{ pools, length }; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Component &, const Component &); * bool(const Entity, const Entity); * @endcode * * Where `Component` are either owned types or not but still such that they * are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Component Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) const { auto* cpool = std::get<0>(pools); if constexpr (sizeof...(Component) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); cpool->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); } else if constexpr (sizeof...(Component) == 1) { cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare((std::get*>(pools)->get(lhs), ...), (std::get*>(pools)->get(rhs), ...)); }, std::move(algo), std::forward(args)...); } else { cpool->sort_n(*length, [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { return compare(std::forward_as_tuple(std::get*>(pools)->get(lhs)...), std::forward_as_tuple(std::get*>(pools)->get(rhs)...)); }, std::move(algo), std::forward(args)...); } [this](auto* head, auto *... other) { for (auto next = *length; next; --next) { const auto pos = next - 1; [[maybe_unused]] const auto entt = head->data()[pos]; (other->swap(other->data()[pos], entt), ...); } }(std::get*>(pools)...); } private: const std::tuple *..., storage_type *...> pools; const size_type* const length; }; } #endif // #include "poly_storage.hpp" #ifndef ENTT_ENTITY_POLY_STORAGE_HPP #define ENTT_ENTITY_POLY_STORAGE_HPP #include #include // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../poly/poly.hpp" #ifndef ENTT_POLY_POLY_HPP #define ENTT_POLY_POLY_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif namespace entt { /** * @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 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; using vtable_type = const void* (const operation, const basic_any&, void*); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template [[nodiscard]] static constexpr policy type_to_policy() { if constexpr (std::is_lvalue_reference_v) { if constexpr (std::is_const_v>) { return policy::CREF; } else { return policy::REF; } } else { return policy::OWNER; } } template [[nodiscard]] static bool compare(const void* lhs, const void* rhs) { if constexpr (!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); } else { return lhs == rhs; } } template 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>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { const Type* instance = (in_situ && from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); switch (op) { case operation::COPY: if constexpr (std::is_copy_constructible_v) { static_cast(to)->emplace(*instance); } break; case operation::MOVE: if constexpr (in_situ) { if (from.mode == policy::OWNER) { return new (&static_cast(to)->storage) Type{ std::move(*const_cast(instance)) }; } } return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); case operation::DTOR: if (from.mode == policy::OWNER) { if constexpr (in_situ) { instance->~Type(); } else if constexpr (std::is_array_v) { delete[] instance; } else { delete instance; } } break; case operation::COMP: return compare(instance, (*static_cast(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(to) = type_id(); break; } } return nullptr; } template void initialize([[maybe_unused]] Args &&... args) { if constexpr (!std::is_void_v) { if constexpr (std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); instance = (std::addressof(args), ...); } else if constexpr (in_situ) { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { new (&storage) Type{ std::forward(args)... }; } else { new (&storage) Type(std::forward(args)...); } } else { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { instance = new Type{ std::forward(args)... }; } else { instance = new Type(std::forward(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 }, 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 explicit basic_any(std::in_place_type_t, Args &&... args) : instance{}, vtable{ &basic_vtable>> }, mode{ type_to_policy() } { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper 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 basic_any(std::reference_wrapper value) ENTT_NOEXCEPT : basic_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, basic_any>>> basic_any(Type&& value) : instance{}, vtable{ &basic_vtable> }, mode{ policy::OWNER } { initialize>(std::forward(value)); } /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any& other) : instance{}, vtable{ &basic_vtable }, 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 [[deprecated("Use std::in_place_type, entt::make_any, emplace or forward_as_any instead")]] basic_any& operator=(std::reference_wrapper value) ENTT_NOEXCEPT { emplace(value.get()); 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 std::enable_if_t, basic_any>, basic_any&> operator=(Type&& value) { emplace>(std::forward(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(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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, *this, nullptr); mode = type_to_policy(); initialize(std::forward(args)...); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(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; }; /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ template [[nodiscard]] inline bool operator!=(const basic_any& lhs, const basic_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @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 Type any_cast(const basic_any& data) ENTT_NOEXCEPT { const auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any&& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } /*! @copydoc any_cast */ template const Type* any_cast(const basic_any* data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ template Type* any_cast(basic_any* data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } /** * @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::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&... args) { return basic_any{std::in_place_type, std::forward(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::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type&& value) { return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } } #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "hashed_string.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "fwd.hpp" #ifndef ENTT_POLY_FWD_HPP #define ENTT_POLY_FWD_HPP #include namespace entt { template)> class basic_poly; /** * @brief Alias declaration for the most common use case. * @tparam Concept Concept descriptor. */ template using poly = basic_poly; } #endif namespace entt { /*! @brief Inspector class used to infer the type of the virtual table. */ struct poly_inspector { /** * @brief Generic conversion operator (definition only). * @tparam Type Type to which conversion is requested. */ template operator Type && () const; /** * @brief Dummy invocation function (definition only). * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param args The arguments to pass to the function. * @return A poly inspector convertible to any type. */ template poly_inspector invoke(Args &&... args) const; /*! @copydoc invoke */ template poly_inspector invoke(Args &&... args); }; /** * @brief Static virtual table factory. * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. */ template class poly_vtable { using inspector = typename Concept::template type; template static auto vtable_entry(Ret(*)(inspector&, Args...))->Ret(*)(basic_any&, Args...); template static auto vtable_entry(Ret(*)(const inspector&, Args...))->Ret(*)(const basic_any&, Args...); template static auto vtable_entry(Ret(*)(Args...))->Ret(*)(const basic_any&, Args...); template static auto vtable_entry(Ret(inspector::*)(Args...))->Ret(*)(basic_any&, Args...); template static auto vtable_entry(Ret(inspector::*)(Args...) const)->Ret(*)(const basic_any&, Args...); template static auto make_vtable(value_list) -> decltype(std::make_tuple(vtable_entry(Candidate)...)); template [[nodiscard]] static constexpr auto make_vtable(type_list) { if constexpr (sizeof...(Func) == 0) { return decltype(make_vtable(typename Concept::template impl{})){}; } else if constexpr ((std::is_function_v && ...)) { return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; } } template static void fill_vtable_entry(Ret(*&entry)(Any&, Args...)) { if constexpr (std::is_invocable_r_v) { entry = +[](Any&, Args... args) -> Ret { return std::invoke(Candidate, std::forward(args)...); }; } else { entry = +[](Any& instance, Args... args) -> Ret { return static_cast(std::invoke(Candidate, any_cast&>(instance), std::forward(args)...)); }; } } template [[nodiscard]] static auto fill_vtable(std::index_sequence) { type impl{}; (fill_vtable_entry>>(std::get(impl)), ...); return impl; } public: /*! @brief Virtual table type. */ using type = decltype(make_vtable(Concept{})); /** * @brief Returns a static virtual table for a specific concept and type. * @tparam Type The type for which to generate the virtual table. * @return A static virtual table for the given concept and type. */ template [[nodiscard]] static const auto* instance() { static_assert(std::is_same_v>, "Type differs from its decayed form"); static const auto vtable = fill_vtable(std::make_index_sequence::size>{}); return &vtable; } }; /** * @brief Poly base class used to inject functionalities into concepts. * @tparam Poly The outermost poly class. */ template struct poly_base { /** * @brief Invokes a function from the static virtual table. * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template [[nodiscard]] decltype(auto) invoke(const poly_base& self, Args &&... args) const { const auto& poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } /*! @copydoc invoke */ template [[nodiscard]] decltype(auto) invoke(poly_base& self, Args &&... args) { auto& poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } }; /** * @brief Shortcut for calling `poly_base::invoke`. * @tparam Member Index of the function to invoke. * @tparam Poly A fully defined poly object. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template decltype(auto) poly_call(Poly&& self, Args &&... args) { return std::forward(self).template invoke(self, std::forward(args)...); } /** * @brief Static polymorphism made simple and within everyone's reach. * * Static polymorphism is a very powerful tool in C++, albeit sometimes * cumbersome to obtain.
* This class aims to make it simple and easy to use. * * @note * Both deduced and defined static virtual tables are supported.
* Moreover, the `poly` class template also works with unmanaged objects. * * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_poly : private Concept::template type>> { /*! @brief A poly base is allowed to snoop into a poly object. */ friend struct poly_base; using vtable_type = typename poly_vtable::type; public: /*! @brief Concept type. */ using concept_type = typename Concept::template type>; /*! @brief Default constructor. */ basic_poly() ENTT_NOEXCEPT : storage{}, vtable{} {} /** * @brief Constructs a poly by directly initializing the new object. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_poly(std::in_place_type_t, Args &&... args) : storage{ std::in_place_type, std::forward(args)... }, vtable{ poly_vtable::template instance>>() } {} /** * @brief Constructs a poly from a given value. * @tparam Type Type of object to use to initialize the poly. * @param value An instance of an object to use to initialize the poly. */ template>, basic_poly>>> basic_poly(Type&& value) ENTT_NOEXCEPT : basic_poly{ std::in_place_type>>, std::forward(value) } {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_poly(const basic_poly& other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ basic_poly(basic_poly&& other) ENTT_NOEXCEPT : basic_poly{} { swap(*this, other); } /** * @brief Assignment operator. * @param other The instance to assign from. * @return This poly object. */ basic_poly& operator=(basic_poly other) { swap(other, *this); 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 { return storage.type(); } /** * @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 Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&... args) { *this = basic_poly{ std::in_place_type, std::forward(args)... }; } /*! @brief Destroys contained object */ void reset() { *this = basic_poly{}; } /** * @brief Returns false if a poly is empty, true otherwise. * @return False if the poly is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(vtable == nullptr); } /** * @brief Returns a pointer to the underlying concept. * @return A pointer to the underlying concept. */ [[nodiscard]] concept_type* operator->() ENTT_NOEXCEPT { return this; } /*! @copydoc operator-> */ [[nodiscard]] const concept_type* operator->() const ENTT_NOEXCEPT { return this; } /** * @brief Swaps two poly objects. * @param lhs A valid poly object. * @param rhs A valid poly object. */ friend void swap(basic_poly& lhs, basic_poly& rhs) { using std::swap; swap(lhs.storage, rhs.storage); swap(lhs.vtable, rhs.vtable); } /** * @brief Aliasing constructor. * @return A poly that shares a reference to an unmanaged object. */ [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { basic_poly ref = std::as_const(*this).as_ref(); ref.storage = storage.as_ref(); return ref; } /*! @copydoc as_ref */ [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; return ref; } private: basic_any storage; const vtable_type* vtable; }; } #endif // #include "fwd.hpp" namespace entt { /** * @brief Basic poly storage implementation. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct Storage : type_list { /** * @brief Concept definition. * @tparam Base Opaque base class from which to inherit. */ template struct type : Base { /** * @brief Returns a type info for the contained objects. * @return The type info for the contained objects. */ type_info value_type() const ENTT_NOEXCEPT { return poly_call<0>(*this); } }; /** * @brief Concept implementation. * @tparam Type Type for which to generate an implementation. */ template using impl = value_list<&type_id>; }; /** * @brief Defines the poly storage type associate with a given entity type. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct poly_storage_traits { /*! @brief Poly storage type for the given entity type. */ using storage_type = poly>; }; } #endif // #include "runtime_view.hpp" #ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP #define ENTT_ENTITY_RUNTIME_VIEW_HPP #include #include #include #include #include // #include "../config/config.h" // #include "entity.hpp" // #include "sparse_set.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Runtime view. * * Runtime views iterate over those entities that have at least all the given * components in their bags. During initialization, a runtime view looks at the * number of entities available for each component and picks up a reference to * the smallest set of candidate entities in order to get a performance boost * when iterate.
* Order of elements during iterations are highly dependent on the order of the * underlying data structures. See sparse_set and its specializations for more * details. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by the views, unless * a pool was missing when the view was built (in this case, the view won't * have a valid reference and won't be updated accordingly). * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_runtime_view final { using basic_common_type = basic_sparse_set; using underlying_iterator = typename basic_common_type::iterator; class view_iterator final { [[nodiscard]] bool valid() const { const auto entt = *it; return (!stable_storage || (entt != tombstone)) && std::all_of(pools->begin()++, pools->end(), [entt](const auto* curr) { return curr->contains(entt); }) && std::none_of(filter->cbegin(), filter->cend(), [entt](const auto* curr) { return curr && curr->contains(entt); }); } public: using difference_type = typename underlying_iterator::difference_type; using value_type = typename underlying_iterator::value_type; using pointer = typename underlying_iterator::pointer; using reference = typename underlying_iterator::reference; using iterator_category = std::bidirectional_iterator_tag; view_iterator() ENTT_NOEXCEPT = default; view_iterator(const std::vector& cpools, const std::vector& ignore, underlying_iterator curr) ENTT_NOEXCEPT : pools{ &cpools }, filter{ &ignore }, it{ curr }, stable_storage{ std::any_of(pools->cbegin(), pools->cend(), [](const basic_common_type* cpool) { return (cpool->policy() == deletion_policy::in_place); }) } { if (it != (*pools)[0]->end() && !valid()) { ++(*this); } } view_iterator& operator++() { while (++it != (*pools)[0]->end() && !valid()); return *this; } view_iterator operator++(int) { view_iterator orig = *this; return ++(*this), orig; } view_iterator& operator--() ENTT_NOEXCEPT { while (--it != (*pools)[0]->begin() && !valid()); return *this; } view_iterator operator--(int) ENTT_NOEXCEPT { view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] bool operator==(const view_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const view_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] pointer operator->() const { return it.operator->(); } [[nodiscard]] reference operator*() const { return *operator->(); } private: const std::vector* pools; const std::vector* filter; underlying_iterator it; bool stable_storage; }; [[nodiscard]] bool valid() const { return !pools.empty() && pools.front(); } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ using iterator = view_iterator; /*! @brief Default constructor to use to create empty, invalid views. */ basic_runtime_view() ENTT_NOEXCEPT : pools{}, filter{} {} /** * @brief Constructs a runtime view from a set of storage classes. * @param cpools The storage for the types to iterate. * @param epools The storage for the types used to filter the view. */ basic_runtime_view(std::vector cpools, std::vector epools) ENTT_NOEXCEPT : pools{ std::move(cpools) }, filter{ std::move(epools) } { // brings the best candidate (if any) on front of the vector std::rotate(pools.begin(), std::min_element(pools.begin(), pools.end(), [](const auto* lhs, const auto* rhs) { return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); }), pools.end()); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const { return valid() ? pools.front()->size() : size_type{}; } /** * @brief Returns an iterator to the first entity that has the given * components. * * The returned iterator points to the first entity that has the given * components. If the view is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity that has the given components. */ [[nodiscard]] iterator begin() const { return valid() ? iterator{ pools, filter, pools[0]->begin() } : iterator{}; } /** * @brief Returns an iterator that is past the last entity that has the * given components. * * The returned iterator points to the entity following the last entity that * has the given components. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity that has the * given components. */ [[nodiscard]] iterator end() const { return valid() ? iterator{ pools, filter, pools[0]->end() } : iterator{}; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto* curr) { return curr->contains(entt); }) && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto* curr) { return curr && curr->contains(entt); }); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity. It is provided only with * the entity itself. To get the components, users can use the registry with * which the view was built.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (const auto entity : *this) { func(entity); } } private: std::vector pools; std::vector filter; }; } #endif // #include "sparse_set.hpp" // #include "storage.hpp" // #include "utility.hpp" // #include "view.hpp" #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" // #include "utility.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class view_iterator final { using basic_common_type = basic_sparse_set::value_type>; [[nodiscard]] bool valid() const { const auto entt = *it; return Policy::accept(entt) && std::apply([entt](const auto *... curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *... curr) { return (!curr->contains(entt) && ...); }, filter); } public: using iterator_type = It; using difference_type = typename std::iterator_traits::difference_type; using value_type = typename std::iterator_traits::value_type; using pointer = typename std::iterator_traits::pointer; using reference = typename std::iterator_traits::reference; using iterator_category = std::bidirectional_iterator_tag; view_iterator() ENTT_NOEXCEPT : first{}, last{}, it{}, pools{}, filter{} {} view_iterator(It from, It to, It curr, std::array all_of, std::array none_of) ENTT_NOEXCEPT : first{ from }, last{ to }, it{ curr }, pools{ all_of }, filter{ none_of } { if (it != last && !valid()) { ++(*this); } } view_iterator& operator++() ENTT_NOEXCEPT { while (++it != last && !valid()); return *this; } view_iterator operator++(int) ENTT_NOEXCEPT { view_iterator orig = *this; return ++(*this), orig; } view_iterator& operator--() ENTT_NOEXCEPT { while (--it != first && !valid()); return *this; } view_iterator operator--(int) ENTT_NOEXCEPT { view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] bool operator==(const view_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const view_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] pointer operator->() const { return &*it; } [[nodiscard]] reference operator*() const { return *operator->(); } private: It first; It last; It it; std::array pools; std::array filter; }; } /** * Internal details not to be documented. * @endcond */ /*! @brief Stable storage policy, aimed at pointer stability. */ struct stable_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity entity) ENTT_NOEXCEPT { return entity != tombstone; } /** * Internal details not to be documented. * @endcond */ }; /*! @brief Packed storage policy, aimed at faster linear iteration. */ struct packed_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity) ENTT_NOEXCEPT { return true; } /** * Internal details not to be documented. * @endcond */ }; /** * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_view_impl; /*! @brief View implementation dispatcher. */ template struct basic_view; /** * @brief Multi component view. * * Multi component views iterate over those entities that have at least all the * given components in their bags. During initialization, a multi component view * looks at the number of entities available for each component and uses the * smallest set in order to get a performance boost when iterate. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Policy Common (stricter) storage policy. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template class basic_view_impl, Component...> { using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Comp>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; iterable_iterator(It from, const basic_view_impl* parent) ENTT_NOEXCEPT : it{ from }, view{ parent } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat(std::make_tuple(*it), view->get(*it)); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; const basic_view_impl* view; }; public: using iterator = iterable_iterator>; using reverse_iterator = iterable_iterator>; iterable(const basic_view_impl& parent) : view{ parent } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return { view.begin(), &view }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return { view.end(), &view }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return { view.rbegin(), &view }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return { view.rend(), &view }; } private: const basic_view_impl view; }; [[nodiscard]] const auto* candidate() const ENTT_NOEXCEPT { return (std::min)({ static_cast(std::get*>(pools))... }, [](const auto* lhs, const auto* rhs) { return lhs->size() < rhs->size(); }); } [[nodiscard]] auto pools_to_unchecked_array() const ENTT_NOEXCEPT { std::size_t pos{}; std::array other{}; (static_cast(std::get*>(pools) == view ? void() : void(other[pos++] = std::get*>(pools))), ...); return other; } [[nodiscard]] auto filter_to_array() const ENTT_NOEXCEPT { return std::array{std::get*>(filter)...}; } template [[nodiscard]] auto dispatch_get([[maybe_unused]] It& it, [[maybe_unused]] const Entity entt) const { if constexpr (std::is_same_v::value_type, typename storage_type::value_type>) { return std::forward_as_tuple(*it); } else { return get_as_tuple(*std::get*>(pools), entt); } } template void traverse(Func func) const { if constexpr (std::is_void_v *>(pools)->get({})) > ) { for (const auto entt : static_cast(*std::get*>(pools))) { if (Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } } else { auto it = std::get*>(pools)->begin(); for (const auto entt : static_cast(*std::get*>(pools))) { if (Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); } else { std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); } } ++it; } } } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param component The storage for the types to iterate. * @param epool The storage for the types used to filter the view. */ basic_view_impl(storage_type &... component, const storage_type &... epool) ENTT_NOEXCEPT : pools{ &component... }, filter{ &epool... }, view{ candidate() } {} /** * @brief Forces the type to use to drive iterations. * @tparam Comp Type of component to use to drive the iteration. */ template void use() const ENTT_NOEXCEPT { view = std::get*>(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { return view->size(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const { return iterator{ view->begin(), view->end(), view->begin(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const { return iterator{ view->begin(), view->end(), view->end(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const { return reverse_iterator{ view->rbegin(), view->rend(), view->rbegin(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const { return reverse_iterator{ view->rbegin(), view->rend(), view->rend(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = iterator{ view->begin(), view->end(), view->find(entt), pools_to_unchecked_array(), filter_to_array() }; return (it != end() && *it == entt) ? it : end(); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return view != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return (std::get*>(pools)->contains(entt) && ...) && (!std::get*>(filter)->contains(entt) && ...); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr (sizeof...(Comp) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Comp) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { ((std::get*>(pools) == view ? traverse(std::move(func)) : void()), ...); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The pool of the suggested component is used to lead the iterations. The * returned entities will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { use(); traverse(std::move(func)); } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{ *this }; } /** * @brief Returns an iterable object to use to _visit_ the view. * * The pool of the suggested component is used to lead the iterations. The * returned elements will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @return An iterable object to use to _visit_ the view. */ template [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { use(); return iterable_view{ *this }; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...>&, const basic_view, CRhs...>&); private: const std::tuple *...> pools; const std::tuple *...> filter; mutable const basic_common_type* view; }; /** * @brief Single component view specialization. * * Single component views are specialized in order to get a boost in terms of * performance. This kind of views can access the underlying data structure * directly and avoid superfluous checks. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given component are created and assigned to entities. * * The entity currently pointed is modified (as an example, the given * component is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Views share a reference to the underlying data structure of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Component Type of component iterated by the view. */ template class basic_view_impl, Component> { using basic_common_type = basic_sparse_set; using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It... from, Discard...) ENTT_NOEXCEPT : it{ from... } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return (++std::get(it), ...), * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return { *std::get(it)... }; } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return std::get<0>(other.it) == std::get<0>(it); } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: std::tuple it; }; public: using iterator = std::conditional_t < std::is_void_v().get({})) > , iterable_iterator, iterable_iterator().begin())> > ; using reverse_iterator = std::conditional_t < std::is_void_v().get({})) > , iterable_iterator, iterable_iterator().rbegin())> > ; iterable(storage_type& ref) : pool{ &ref } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ pool->basic_common_type::begin(), pool->begin() }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{ pool->basic_common_type::end(), pool->end() }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return reverse_iterator{ pool->basic_common_type::rbegin(), pool->rbegin() }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return reverse_iterator{ pool->basic_common_type::rend(), pool->rend() }; } private: storage_type* const pool; }; public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view_impl(storage_type& ref) ENTT_NOEXCEPT : pools{ &ref }, filter{} {} /** * @brief Returns the number of entities that have the given component. * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return std::get<0>(pools)->size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return std::get<0>(pools)->empty(); } /** * @brief Direct access to the raw representation offered by the storage. * @return A pointer to the array of components. */ [[nodiscard]] auto raw() const ENTT_NOEXCEPT { return std::get<0>(pools)->raw(); } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return std::get<0>(pools)->data(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::begin(); } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::end(); } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::rbegin(); } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::rend(); } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = std::get<0>(pools)->find(entt); return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return std::get<0>(pools) != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return std::get<0>(pools)->contains(entt); } /** * @brief Returns the component assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The component assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr (sizeof...(Comp) == 0) { return get_as_tuple(*std::get<0>(pools), entt); } else { static_assert(std::is_same_v, "Invalid component type"); return std::get<0>(pools)->get(entt); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a reference to the component if it's a non-empty one. * The _constness_ of the component is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Component &); * void(Component &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if constexpr (std::is_void_v(pools)->get({})) > ) { if constexpr (std::is_invocable_v) { for (auto pos = size(); pos; --pos) { func(); } } else { for (auto entity : *this) { func(entity); } } } else { if constexpr (is_applicable_v) { for (const auto pack : each()) { std::apply(func, pack); } } else { for (auto&& component : *std::get<0>(pools)) { func(component); } } } } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{ *std::get<0>(pools) }; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...>&, const basic_view, CRhs...>&); private: const std::tuple pools; const std::tuple<> filter; }; /** * @brief View implementation dispatcher. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template struct basic_view, Component...> : basic_view_impl>::in_place_delete...>, stable_storage_policy, packed_storage_policy>, Entity, exclude_t, Component...> { /*! @brief Most restrictive storage policy of all component types. */ using storage_policy = std::conditional_t>::in_place_delete...>, stable_storage_policy, packed_storage_policy>; using basic_view_impl, Component...>::basic_view_impl; }; /** * @brief Deduction guide. * @tparam Storage Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Storage &... storage) ->basic_view, entt::exclude_t<>, constness_as_t...>; /** * @brief Combines two views in a _more specific_ one. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @param lhs A valid reference to the first view. * @param rhs A valid reference to the second view. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, CLhs...>& lhs, const basic_view, CRhs...>& rhs) { using view_type = basic_view, CLhs..., CRhs...>; return std::apply([](auto *... storage) { return view_type{ *storage... }; }, std::tuple_cat(lhs.pools, rhs.pools, lhs.filter, rhs.filter)); } } #endif namespace entt { /** * @brief Fast and reliable entity-component system. * * The registry is the core class of the entity-component framework.
* It stores entities and arranges pools of components on a per request basis. * By means of a registry, users can manage entities and components, then create * views or groups to iterate them. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_registry { using traits_type = entt_traits; using poly_storage_type = typename poly_storage_traits::storage_type; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; struct pool_data { poly_storage_type poly; std::unique_ptr pool{}; }; template struct group_handler; template struct group_handler, get_t, Owned...> { static_assert(!std::disjunction_v::in_place_delete...>, "Groups do not support in-place delete"); static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); std::conditional_t current{}; template void maybe_valid_if(basic_registry& owner, const Entity entt) { [[maybe_unused]] const auto cpools = std::make_tuple(owner.assure()...); const auto is_valid = ((std::is_same_v || std::get*>(cpools)->contains(entt)) && ...) && ((std::is_same_v || owner.assure()->contains(entt)) && ...) && ((std::is_same_v || !owner.assure()->contains(entt)) && ...); if constexpr (sizeof...(Owned) == 0) { if (is_valid && !current.contains(entt)) { current.emplace(entt); } } else { if (is_valid && !(std::get<0>(cpools)->index(entt) < current)) { const auto pos = current++; (std::get*>(cpools)->swap(std::get*>(cpools)->data()[pos], entt), ...); } } } void discard_if([[maybe_unused]] basic_registry& owner, const Entity entt) { if constexpr (sizeof...(Owned) == 0) { current.remove(entt); } else { if (const auto cpools = std::make_tuple(owner.assure()...); std::get<0>(cpools)->contains(entt) && (std::get<0>(cpools)->index(entt) < current)) { const auto pos = --current; (std::get*>(cpools)->swap(std::get*>(cpools)->data()[pos], entt), ...); } } } }; struct group_data { std::size_t size; std::unique_ptr group; bool (*owned)(const id_type) ENTT_NOEXCEPT; bool (*get)(const id_type) ENTT_NOEXCEPT; bool (*exclude)(const id_type) ENTT_NOEXCEPT; }; template [[nodiscard]] storage_type* assure() const { static_assert(std::is_same_v>, "Non-decayed types not allowed"); const auto index = type_seq::value(); if (!(index < pools.size())) { pools.resize(size_type(index) + 1u); } if (auto&& pdata = pools[index]; !pdata.pool) { pdata.pool.reset(new storage_type()); pdata.poly.template emplace&>(*static_cast *>(pdata.pool.get())); } return static_cast *>(pools[index].pool.get()); } template [[nodiscard]] const storage_type* pool_if_exists() const ENTT_NOEXCEPT { static_assert(std::is_same_v>, "Non-decayed types not allowed"); const auto index = type_seq::value(); return (!(index < pools.size()) || !pools[index].pool) ? nullptr : static_cast *>(pools[index].pool.get()); } auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { ENTT_ASSERT(pos < traits_type::to_integral(null), "No entities available"); return traits_type::construct(static_cast(pos), {}); } auto recycle_identifier() ENTT_NOEXCEPT { ENTT_ASSERT(free_list != null, "No entities available"); const auto curr = traits_type::to_entity(free_list); free_list = (tombstone | entities[curr]); return (entities[curr] = traits_type::construct(curr, traits_type::to_version(entities[curr]))); } auto release_entity(const Entity entity, const typename traits_type::version_type version) { const typename traits_type::version_type vers = version + (version == traits_type::to_version(tombstone)); entities[traits_type::to_entity(entity)] = traits_type::construct(traits_type::to_entity(free_list), vers); free_list = (tombstone | entity); return vers; } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Underlying version type. */ using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Poly storage type. */ using poly_storage = typename poly_storage_traits::storage_type; /** * @brief Returns the entity identifier without the version. * @param entity An entity identifier, either valid or not. * @return The entity identifier without the version. */ [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { return traits_type::construct(traits_type::to_entity(entity), {}); } /** * @brief Returns the version stored along with an entity identifier. * @param entity An entity identifier, either valid or not. * @return The version stored along with the given entity identifier. */ [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { return traits_type::to_version(entity); } /*! @brief Default constructor. */ basic_registry() = default; /*! @brief Default move constructor. */ basic_registry(basic_registry&&) = default; /*! @brief Default move assignment operator. @return This registry. */ basic_registry& operator=(basic_registry&&) = default; /** * @brief Prepares a pool for the given type if required. * @tparam Component Type of component for which to prepare a pool. */ template void prepare() { // suppress the warning due to the [[nodiscard]] attribute static_cast(assure()); } /** * @brief Returns a poly storage for a given type. * @param info The type for which to return a poly storage. * @return A valid poly storage if a pool for the given type exists, an * empty and thus invalid element otherwise. */ poly_storage& storage(const type_info info) { ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly, "Storage not available"); return pools[info.seq()].poly; } /*! @copydoc storage */ const poly_storage& storage(const type_info info) const { ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly, "Storage not available"); return pools[info.seq()].poly; } /** * @brief Returns the number of existing components of the given type. * @tparam Component Type of component of which to return the size. * @return Number of existing components of the given type. */ template [[nodiscard]] size_type size() const { const auto* cpool = pool_if_exists(); return cpool ? cpool->size() : size_type{}; } /** * @brief Returns the number of entities created so far. * @return Number of entities created so far. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return entities.size(); } /** * @brief Returns the number of entities still in use. * @return Number of entities still in use. */ [[nodiscard]] size_type alive() const { auto sz = entities.size(); for (auto curr = free_list; curr != null; --sz) { curr = entities[traits_type::to_entity(curr)]; } return sz; } /** * @brief Increases the capacity of the registry or of the pools for the * given components. * * If no components are specified, the capacity of the registry is * increased, that is the number of entities it contains. Otherwise the * capacity of the pools for the given components is increased.
* In both cases, if the new capacity is greater than the current capacity, * new storage is allocated, otherwise the method does nothing. * * @tparam Component Types of components for which to reserve storage. * @param cap Desired capacity. */ template void reserve(const size_type cap) { if constexpr (sizeof...(Component) == 0) { entities.reserve(cap); } else { (assure()->reserve(cap), ...); } } /** * @brief Reserves enough space to store `count` pools. * @param count Number of pools to reserve space for. */ [[deprecated("No longer supported")]] void reserve_pools(const size_t count) { pools.reserve(count); } /** * @brief Returns the capacity of the pool for the given component. * @tparam Component Type of component in which one is interested. * @return Capacity of the pool of the given component. */ template [[nodiscard]] size_type capacity() const { const auto* cpool = pool_if_exists(); return cpool ? cpool->capacity() : size_type{}; } /** * @brief Returns the number of entities that a registry has currently * allocated space for. * @return Capacity of the registry. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return entities.capacity(); } /** * @brief Requests the removal of unused capacity for the given components. * @tparam Component Types of components for which to reclaim unused * capacity. */ template void shrink_to_fit() { (assure()->shrink_to_fit(), ...); } /** * @brief Checks whether the registry or the pools of the given components * are empty. * * A registry is considered empty when it doesn't contain entities that are * still in use. * * @tparam Component Types of components in which one is interested. * @return True if the registry or the pools of the given components are * empty, false otherwise. */ template [[nodiscard]] bool empty() const { if constexpr (sizeof...(Component) == 0) { return !alive(); } else { return [](const auto *... cpool) { return ((!cpool || cpool->empty()) && ...); }(pool_if_exists()...); } } /** * @brief Direct access to the list of entities of a registry. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @warning * This list contains both valid and destroyed entities and isn't suitable * for direct use. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type* data() const ENTT_NOEXCEPT { return entities.data(); } /** * @brief Returns the head of the list of released entities. * * This function is intended for use in conjunction with `assign`.
* The returned entity has an invalid identifier in all cases. * * @return The head of the list of released entities. */ [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { return free_list; } /*! @copydoc released */ [[deprecated("Use ::released instead")]] [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT { return released(); } /** * @brief Checks if an entity identifier refers to a valid entity. * @param entity An entity identifier, either valid or not. * @return True if the identifier is valid, false otherwise. */ [[nodiscard]] bool valid(const entity_type entity) const { const auto pos = size_type(traits_type::to_entity(entity)); return (pos < entities.size() && entities[pos] == entity); } /** * @brief Returns the actual version for an entity identifier. * * @warning * Attempting to use an entity that doesn't belong to the registry results * in undefined behavior. An entity belongs to the registry even if it has * been previously destroyed and/or recycled. * * @param entity A valid entity identifier. * @return Actual version for the given entity identifier. */ [[nodiscard]] version_type current(const entity_type entity) const { const auto pos = size_type(traits_type::to_entity(entity)); ENTT_ASSERT(pos < entities.size(), "Entity does not exist"); return version(entities[pos]); } /** * @brief Creates a new entity and returns it. * * There are two kinds of possible entity identifiers: * * * Newly created ones in case no entities have been previously destroyed. * * Recycled ones with updated versions. * * @return A valid entity identifier. */ [[nodiscard]] entity_type create() { return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); } /** * @brief Creates a new entity and returns it. * * @sa create * * If the requested entity isn't in use, the suggested identifier is created * and returned. Otherwise, a new identifier is generated. * * @param hint Required entity identifier. * @return A valid entity identifier. */ [[nodiscard]] entity_type create(const entity_type hint) { const auto length = entities.size(); if (hint == null || hint == tombstone) { return create(); } else if (const auto req = traits_type::to_entity(hint); !(req < length)) { entities.resize(size_type(req) + 1u, null); for (auto pos = length; pos < req; ++pos) { release_entity(generate_identifier(pos), {}); } return (entities[req] = hint); } else if (const auto curr = traits_type::to_entity(entities[req]); req == curr) { return create(); } else { auto* it = &free_list; for (; traits_type::to_entity(*it) != req; it = &entities[traits_type::to_entity(*it)]); *it = traits_type::construct(curr, traits_type::to_version(*it)); return (entities[req] = hint); } } /** * @brief Assigns each element in a range an entity. * * @sa create * * @tparam It Type of forward iterator. * @param first An iterator to the first element of the range to generate. * @param last An iterator past the last element of the range to generate. */ template void create(It first, It last) { for (; free_list != null && first != last; ++first) { *first = recycle_identifier(); } const auto length = entities.size(); entities.resize(length + std::distance(first, last), null); for (auto pos = length; first != last; ++first, ++pos) { *first = entities[pos] = generate_identifier(pos); } } /** * @brief Assigns entities to an empty registry. * * This function is intended for use in conjunction with `data`, `size` and * `destroyed`.
* Don't try to inject ranges of randomly generated entities nor the _wrong_ * head for the list of destroyed entities. There is no guarantee that a * registry will continue to work properly in this case. * * @warning * There must be no entities still alive for this to work properly. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param destroyed The head of the list of destroyed entities. */ template void assign(It first, It last, const entity_type destroyed) { ENTT_ASSERT(!alive(), "Entities still alive"); entities.assign(first, last); free_list = destroyed; } /** * @brief Releases an entity identifier. * * The version is updated and the identifier can be recycled at any time. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. * @return The version of the recycled entity. */ version_type release(const entity_type entity) { return release(entity, version(entity) + 1u); } /** * @brief Releases an entity identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa release * * @param entity A valid entity identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type release(const entity_type entity, const version_type version) { ENTT_ASSERT(orphan(entity), "Non-orphan entity"); return release_entity(entity, version); } /** * @brief Releases all entity identifiers in a range. * * @sa release * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void release(It first, It last) { for (; first != last; ++first) { release(*first, version(*first) + 1u); } } /** * @brief Destroys an entity and releases its identifier. * * The version is updated and the identifier can be recycled at any time. * * @warning * Adding or removing components to an entity that is being destroyed can * result in undefined behavior. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. * @return The version of the recycled entity. */ version_type destroy(const entity_type entity) { return destroy(entity, version(entity) + 1u); } /** * @brief Destroys an entity and releases its identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa destroy * * @param entity A valid entity identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type destroy(const entity_type entity, const version_type version) { ENTT_ASSERT(valid(entity), "Invalid entity"); for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(entity, this); } return release_entity(entity, version); } /** * @brief Destroys all entities in a range and releases their identifiers. * * @sa destroy * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void destroy(It first, It last) { if constexpr (is_iterator_type_v) { for (; first != last; ++first) { destroy(*first, version(*first) + 1u); } } else { for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(first, last, this); } release(first, last); } } /** * @brief Assigns the given component to an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of * aggregate type). Then the component is assigned to the given entity. * * @warning * Attempting to use an invalid entity or to assign a component to an entity * that already owns it results in undefined behavior. * * @tparam Component Type of component to create. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); return assure()->emplace(*this, entity, std::forward(args)...); } /** * @brief Assigns each entity in a range the given component. * * @sa emplace * * @tparam Component Type of component to create. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the component to assign. */ template void insert(It first, It last, const Component& value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); assure()->insert(*this, first, last, value); } /** * @brief Assigns each entity in a range the given components. * * @sa emplace * * @tparam Component Type of component to create. * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of components. */ template::value_type>, Component>>> void insert(EIt first, EIt last, CIt from) { static_assert(std::is_constructible_v::value_type>, "Invalid value type"); ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); assure()->insert(*this, first, last, from); } /** * @brief Assigns or replaces the given component for an entity. * * Equivalent to the following snippet (pseudocode): * * @code{.cpp} * auto &component = registry.all_of(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); auto* cpool = assure(); return cpool->contains(entity) ? cpool->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{ std::forward(args)... }), ...); }) : cpool->emplace(*this, entity, std::forward(args)...); } /** * @brief Patches the given component for an entity. * * The signature of the functions should be equivalent to the following: * * @code{.cpp} * void(Component &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned. However, this function can be used to trigger an update signal * for them. * * @warning * Attempting to use an invalid entity or to patch a component of an entity * that doesn't own it results in undefined behavior. * * @tparam Component Type of component to patch. * @tparam Func Types of the function objects to invoke. * @param entity A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched component. */ template decltype(auto) patch(const entity_type entity, Func &&... func) { ENTT_ASSERT(valid(entity), "Invalid entity"); return assure()->patch(*this, entity, std::forward(func)...); } /** * @brief Replaces the given component for an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of * aggregate type). Then the component is assigned to the given entity. * * @warning * Attempting to use an invalid entity or to replace a component of an * entity that doesn't own it results in undefined behavior. * * @tparam Component Type of component to replace. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ template decltype(auto) replace(const entity_type entity, Args &&... args) { return assure()->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{ std::forward(args)... }), ...); }); } /** * @brief Removes the given components from an entity. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Types of components to remove. * @param entity A valid entity identifier. * @return The number of components actually removed. */ template size_type remove(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); return (assure()->remove(entity, this) + ... + size_type{}); } /** * @brief Removes the given components from all the entities in a range. * * @sa remove * * @tparam Component Types of components to remove. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return The number of components actually removed. */ template size_type remove(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); const auto cpools = std::make_tuple(assure()...); size_type count{}; for (; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); count += (std::get*>(cpools)->remove(entity, this) + ...); } return count; } /** * @brief Erases the given components from an entity. * * @warning * Attempting to use an invalid entity or to erase a component from an * entity that doesn't own it results in undefined behavior. * * @tparam Component Types of components to erase. * @param entity A valid entity identifier. */ template void erase(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); (assure()->erase(entity, this), ...); } /** * @brief Erases the given components from all the entities in a range. * * @sa erase * * @tparam Component Types of components to erase. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void erase(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); const auto cpools = std::make_tuple(assure()...); for (; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); (std::get*>(cpools)->erase(entity, this), ...); } } /** * @brief Removes all tombstones from a registry or only the pools for the * given components. * @tparam Component Types of components for which to clear all tombstones. */ template void compact() { if constexpr (sizeof...(Component) == 0) { for (auto&& pdata : pools) { pdata.pool && (pdata.pool->compact(), true); } } else { (assure()->compact(), ...); } } /*! @copydoc remove */ template [[deprecated("Use ::remove instead")]] size_type remove_if_exists(const entity_type entity) { return remove(entity); } /** * @brief Removes all the components from an entity and makes it orphaned. * * @warning * In case there are listeners that observe the destruction of components * and assign other components to the entity in their bodies, the result of * invoking this function may not be as expected. In the worst case, it * could lead to undefined behavior. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. */ [[deprecated("Use ::destroy(entity)/::create(entity) instead")]] void remove_all(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(entity, this); } } /** * @brief Checks if an entity has all the given components. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. * @return True if the entity has all the components, false otherwise. */ template [[nodiscard]] bool all_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return [entity](const auto *... cpool) { return ((cpool && cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** * @brief Checks if an entity has at least one of the given components. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. * @return True if the entity has at least one of the given components, * false otherwise. */ template [[nodiscard]] bool any_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return [entity](const auto *... cpool) { return !((!cpool || !cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** * @brief Returns references to the given components for an entity. * * @warning * Attempting to use an invalid entity or to get a component from an entity * that doesn't own it results in undefined behavior. * * @tparam Component Types of components to get. * @param entity A valid entity identifier. * @return References to the components owned by the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { const auto* cpool = pool_if_exists...>(); ENTT_ASSERT(cpool, "Storage not available"); return cpool->get(entity); } else { return std::forward_as_tuple(get(entity)...); } } /*! @copydoc get */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { return (const_cast(assure>()->get(entity)), ...); } else { return std::forward_as_tuple(get(entity)...); } } /** * @brief Returns a reference to the given component for an entity. * * In case the entity doesn't own the component, the parameters provided are * used to construct it.
* Equivalent to the following snippet (pseudocode): * * @code{.cpp} * auto &component = registry.all_of(entity) ? registry.get(entity) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Type of component to get. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the entity. */ template [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); auto* cpool = assure(); return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward(args)...); } /** * @brief Returns pointers to the given components for an entity. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @note * The registry retains ownership of the pointed-to components. * * @tparam Component Types of components to get. * @param entity A valid entity identifier. * @return Pointers to the components owned by the entity. */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { const auto* cpool = pool_if_exists...>(); return (cpool && cpool->contains(entity)) ? &cpool->get(entity) : nullptr; } else { return std::make_tuple(try_get(entity)...); } } /*! @copydoc try_get */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { return (const_cast(std::as_const(*this).template try_get(entity)), ...); } else { return std::make_tuple(try_get(entity)...); } } /** * @brief Clears a whole registry or the pools for the given components. * @tparam Component Types of components to remove from their entities. */ template void clear() { if constexpr (sizeof...(Component) == 0) { for (auto&& pdata : pools) { pdata.pool && (pdata.pool->clear(this), true); } each([this](const auto entity) { release_entity(entity, version(entity) + 1u); }); } else { (assure()->clear(this), ...); } } /** * @brief Iterates all the entities that are still in use. * * The function object is invoked for each entity that is still in use.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const Entity); * @endcode * * This function is fairly slow and should not be used frequently. However, * it's useful for iterating all the entities still in use, regardless of * their components. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if (free_list == null) { for (auto pos = entities.size(); pos; --pos) { func(entities[pos - 1]); } } else { for (auto pos = entities.size(); pos; --pos) { if (const auto entity = entities[pos - 1]; traits_type::to_entity(entity) == (pos - 1)) { func(entity); } } } } /** * @brief Checks if an entity has components assigned. * @param entity A valid entity identifier. * @return True if the entity has no components assigned, false otherwise. */ [[nodiscard]] bool orphan(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return std::none_of(pools.cbegin(), pools.cend(), [entity](auto&& pdata) { return pdata.pool && pdata.pool->contains(entity); }); } /** * @brief Iterates orphans and applies them the given function object. * * The function object is invoked for each entity that is still in use and * has no components assigned.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const Entity); * @endcode * * This function can be very slow and should not be used frequently. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void orphans(Func func) const { each([this, &func](const auto entity) { if (orphan(entity)) { func(entity); } }); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever a new instance of the given component is created and assigned to * an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been assigned to the * entity. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_construct() { return assure()->on_construct(); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever an instance of the given component is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been updated. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_update() { return assure()->on_update(); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever an instance of the given component is removed from an entity and * thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **before** the component has been removed from the * entity. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_destroy() { return assure()->on_destroy(); } /** * @brief Returns a view for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Feel free to discard a view after the use. Creating and destroying a view * is an incredibly cheap operation because they do not require any type of * initialization.
* As a rule of thumb, storing a view should never be an option. * * Views do their best to iterate the smallest set of candidate entities. * In particular: * * * Single component views are incredibly fast and iterate a packed array * of entities, all of which has the given component. * * Multi component views look at the number of entities available for each * component and pick up a reference to the smallest set of candidates to * test for the given components. * * Views in no way affect the functionalities of the registry nor those of * the underlying pools. * * @note * Multi component views are pretty fast. However their performance tend to * degenerate when the number of components to iterate grows up and the most * of the entities have all the given components.
* To get a performance boost, consider using a group instead. * * @tparam Component Type of components used to construct the view. * @tparam Exclude Types of components used to filter the view. * @return A newly created view. */ template [[nodiscard]] basic_view, std::add_const_t...> view(exclude_t = {}) const { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); return { *assure>()..., *assure()... }; } /*! @copydoc view */ template [[nodiscard]] basic_view, Component...> view(exclude_t = {}) { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); return { *assure>()..., *assure()... }; } /** * @brief Returns a runtime view for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Users should throw away the view after use. Fortunately, creating and * destroying a runtime view is an incredibly cheap operation because they * do not require any type of initialization.
* As a rule of thumb, storing a view should never be an option. * * Runtime views are to be used when users want to construct a view from * some external inputs and don't know at compile-time what are the required * components. * * @tparam ItComp Type of input iterator for the components to use to * construct the view. * @tparam ItExcl Type of input iterator for the components to use to filter * the view. * @param first An iterator to the first element of the range of components * to use to construct the view. * @param last An iterator past the last element of the range of components * to use to construct the view. * @param from An iterator to the first element of the range of components * to use to filter the view. * @param to An iterator past the last element of the range of components to * use to filter the view. * @return A newly created runtime view. */ template [[nodiscard]] basic_runtime_view runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const { std::vector component(std::distance(first, last)); std::vector filter(std::distance(from, to)); std::transform(first, last, component.begin(), [this](const auto ctype) { const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto&& pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); std::transform(from, to, filter.begin(), [this](const auto ctype) { const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto&& pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); return { std::move(component), std::move(filter) }; } /** * @brief Returns a group for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Feel free to discard a group after the use. Creating and destroying a * group is an incredibly cheap operation because they do not require any * type of initialization, but for the first time they are requested.
* As a rule of thumb, storing a group should never be an option. * * Groups support exclusion lists and can own types of components. The more * types are owned by a group, the faster it is to iterate entities and * components.
* However, groups also affect some features of the registry such as the * creation and destruction of components, which will consequently be * slightly slower (nothing that can be noticed in most cases). * * @note * Pools of components that are owned by a group cannot be sorted anymore. * The group takes the ownership of the pools and arrange components so as * to iterate them as fast as possible. * * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); using handler_type = group_handler, get_t...>, std::remove_const_t...>; const auto cpools = std::make_tuple(assure>()..., assure>()...); constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); handler_type* handler = nullptr; if (auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return gdata.size == size && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash::value()) && ...); }); it != groups.cend()) { handler = static_cast(it->group.get()); } if (!handler) { group_data candidate = { size, { new handler_type{}, [](void* instance) { delete static_cast(instance); } }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, }; handler = static_cast(candidate.group.get()); const void* maybe_valid_if = nullptr; const void* discard_if = nullptr; if constexpr (sizeof...(Owned) == 0) { groups.push_back(std::move(candidate)); } else { ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto& gdata) { const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash::value())); return !overlapping || ((sz == size) || (sz == gdata.size)); }), "Conflicting groups"); const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return !(0u + ... + gdata.owned(type_hash>::value())) || (size > gdata.size); }); const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto& gdata) { return (0u + ... + gdata.owned(type_hash>::value())); }); maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); groups.insert(next, std::move(candidate)); } (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); if constexpr (sizeof...(Owned) == 0) { for (const auto entity : view(exclude)) { handler->current.emplace(entity); } } else { // we cannot iterate backwards because we want to leave behind valid entities in case of owned types for (auto* first = std::get<0>(cpools)->data(), *last = first + std::get<0>(cpools)->size(); first != last; ++first) { handler->template maybe_valid_if...>>>(*this, *first); } } } return { handler->current, *std::get>*>(cpools)..., *std::get>*>(cpools)... }; } /** * @brief Returns a group for the given components. * * @sa group * * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t...>, std::add_const_t...> group_if_exists(get_t, exclude_t = {}) const { if (auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto& gdata) { return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash::value()) && ...); }); it == groups.cend()) { return {}; } else { using handler_type = group_handler, get_t...>, std::remove_const_t...>; return { static_cast(it->group.get())->current, *pool_if_exists>()... , *pool_if_exists>()... }; } } /** * @brief Returns a group for the given components. * * @sa group * * @tparam Owned Types of components owned by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) { return group(get_t<>{}, exclude); } /** * @brief Returns a group for the given components. * * @sa group_if_exists * * @tparam Owned Types of components owned by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t<>, std::add_const_t...> group_if_exists(exclude_t = {}) const { return group_if_exists...>(get_t<>{}, exclude); } /** * @brief Checks whether the given components belong to any group. * @tparam Component Types of components in which one is interested. * @return True if the pools of the given components are sortable, false * otherwise. */ template [[nodiscard]] bool sortable() const { return std::none_of(groups.cbegin(), groups.cend(), [](auto&& gdata) { return (gdata.owned(type_hash>::value()) || ...); }); } /** * @brief Checks whether a group can be sorted. * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return True if the group can be sorted, false otherwise. */ template [[nodiscard]] bool sortable(const basic_group, get_t, Owned...>&) ENTT_NOEXCEPT { constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); return std::find_if(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return (0u + ... + gdata.owned(type_hash>::value())) && (size < gdata.size); }) == groups.cend(); } /** * @brief Sorts the pool of entities for the given component. * * The order of the elements in a pool is highly affected by assignments * of components to entities and deletions. Components are arranged to * maximize the performance during iterations and users should not make any * assumption on the order.
* This function can be used to impose an order to the elements in the pool * of the given component. The order is kept valid until a component of the * given type is assigned or removed from an entity. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(const Entity, const Entity); * bool(const Component &, const Component &); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * The comparison funtion object received by the sort function object hasn't * necessarily the type of the one passed along with the other parameters to * this member function. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam Component Type of components to sort. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); assure()->sort(std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sorts two pools of components in the same way. * * The order of the elements in a pool is highly affected by assignments * of components to entities and deletions. Components are arranged to * maximize the performance during iterations and users should not make any * assumption on the order. * * It happens that different pools of components must be sorted the same way * because of runtime and/or performance constraints. This function can be * used to order a pool of components according to the order between the * entities in another pool of components. * * @b How @b it @b works * * Being `A` and `B` the two sets where `B` is the master (the one the order * of which rules) and `A` is the slave (the one to sort), after a call to * this function an iterator for `A` will return the entities according to * the following rules: * * * All the entities in `A` that are also in `B` are returned first * according to the order they have in `B`. * * All the entities in `A` that are not in `B` are returned in no * particular order after all the other entities. * * Any subsequent change to `B` won't affect the order in `A`. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam To Type of components to sort. * @tparam From Type of components to use to sort. */ template void sort() { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); assure()->respect(*assure()); } /** * @brief Visits an entity and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the components owned by the entity. * * @sa type_info * * @warning * It's not specified whether a component attached to or removed from the * given entity during the visit is returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param entity A valid entity identifier. * @param func A valid function object. */ template void visit(entity_type entity, Func func) const { for (auto pos = pools.size(); pos; --pos) { if (const auto& pdata = pools[pos - 1]; pdata.pool && pdata.pool->contains(entity)) { func(pdata.poly->value_type()); } } } /** * @brief Visits a registry and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the components managed by the registry. * * @sa type_info * * @warning * It's not specified whether a component for which a pool is created during * the visit is returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void visit(Func func) const { for (auto pos = pools.size(); pos; --pos) { if (const auto& pdata = pools[pos - 1]; pdata.pool) { func(pdata.poly->value_type()); } } } /** * @brief Binds an object to the context of the registry. * * If the value already exists it is overwritten, otherwise a new instance * of the given type is created and initialized with the arguments provided. * * @tparam Type Type of object to set. * @tparam Args Types of arguments to use to construct the object. * @param args Parameters to use to initialize the value. * @return A reference to the newly created object. */ template Type& set(Args &&... args) { unset(); vars.emplace_back(std::in_place_type, std::forward(args)...); return any_cast(vars.back()); } /** * @brief Unsets a context variable if it exists. * @tparam Type Type of object to set. */ template void unset() { vars.erase(std::remove_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }), vars.end()); } /** * @brief Binds an object to the context of the registry. * * In case the context doesn't contain the given object, the parameters * provided are used to construct it. * * @tparam Type Type of object to set. * @tparam Args Types of arguments to use to construct the object. * @param args Parameters to use to initialize the object. * @return A reference to the object in the context of the registry. */ template [[nodiscard]] Type& ctx_or_set(Args &&... args) { auto* value = try_ctx(); return value ? *value : set(std::forward(args)...); } /** * @brief Returns a pointer to an object in the context of the registry. * @tparam Type Type of object to get. * @return A pointer to the object if it exists in the context of the * registry, a null pointer otherwise. */ template [[nodiscard]] std::add_const_t* try_ctx() const { auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto&& var) { return var.type() == type; }); return it == vars.cend() ? nullptr : any_cast>(&*it); } /*! @copydoc try_ctx */ template [[nodiscard]] Type* try_ctx() { auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }); return it == vars.end() ? nullptr : any_cast(&*it); } /** * @brief Returns a reference to an object in the context of the registry. * * @warning * Attempting to get a context variable that doesn't exist results in * undefined behavior. * * @tparam Type Type of object to get. * @return A valid reference to the object in the context of the registry. */ template [[nodiscard]] std::add_const_t& ctx() const { auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto&& var) { return var.type() == type; }); ENTT_ASSERT(it != vars.cend(), "Invalid instance"); return any_cast&>(*it); } /*! @copydoc ctx */ template [[nodiscard]] Type& ctx() { auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }); ENTT_ASSERT(it != vars.end(), "Invalid instance"); return any_cast(*it); } /** * @brief Visits a registry and returns the type info for its context * variables. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the context variables currently set. * * @sa type_info * * @warning * It's not specified whether a context variable created during the visit is * returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void ctx(Func func) const { for (auto pos = vars.size(); pos; --pos) { func(vars[pos - 1].type()); } } private: std::vector> vars{}; mutable std::vector pools{}; std::vector groups{}; std::vector entities{}; entity_type free_list{ tombstone }; }; } #endif namespace entt { /** * @brief Non-owning handle to an entity. * * Tiny wrapper around a registry and an entity. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Types to which to restrict the scope of a handle. */ template struct basic_handle { /*! @brief Type of registry accepted by the handle. */ using registry_type = constness_as_t>, Entity>; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename registry_type::version_type; /*! @brief Unsigned integer type. */ using size_type = typename registry_type::size_type; /*! @brief Constructs an invalid handle. */ basic_handle() ENTT_NOEXCEPT : reg{}, entt{ null } {} /** * @brief Constructs a handle from a given registry and entity. * @param ref An instance of the registry class. * @param value An entity identifier. */ basic_handle(registry_type& ref, entity_type value) ENTT_NOEXCEPT : reg{ &ref }, entt{ value } {} /** * @brief Compares two handles. * @tparam Args Template parameters of the handle with which to compare. * @param other Handle with which to compare. * @return True if both handles refer to the same registry and the same * entity, false otherwise. */ template [[nodiscard]] bool operator==(const basic_handle& other) const ENTT_NOEXCEPT { return reg == other.registry() && entt == other.entity(); } /** * @brief Constructs a const handle from a non-const one. * @tparam Other A valid entity type (see entt_traits for more details). * @tparam Args Scope of the handle to construct. * @return A const handle referring to the same registry and the same * entity. */ template operator basic_handle() const ENTT_NOEXCEPT { static_assert( (std::is_same_v || std::is_same_v, Entity>) && (sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles" ); return reg ? basic_handle{*reg, entt} : basic_handle{}; } /** * @brief Converts a handle to its underlying entity. * @return An entity identifier. */ [[nodiscard]] operator entity_type() const ENTT_NOEXCEPT { return entity(); } /** * @brief Checks if a handle refers to non-null registry pointer and entity. * @return True if the handle refers to non-null registry and entity, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return reg && reg->valid(entt); } /** * @brief Checks if a handle refers to a valid entity or not. * @return True if the handle refers to a valid entity, false otherwise. */ [[nodiscard]] bool valid() const { return reg->valid(entt); } /** * @brief Returns a pointer to the underlying registry, if any. * @return A pointer to the underlying registry, if any. */ [[nodiscard]] registry_type* registry() const ENTT_NOEXCEPT { return reg; } /** * @brief Returns the entity associated with a handle. * @return The entity associated with the handle. */ [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { return entt; } /** * @brief Destroys the entity associated with a handle. * @sa basic_registry::destroy */ void destroy() { reg->destroy(entt); } /** * @brief Destroys the entity associated with a handle. * @sa basic_registry::destroy * @param version A desired version upon destruction. */ void destroy(const version_type version) { reg->destroy(entt, version); } /** * @brief Assigns the given component to a handle. * @sa basic_registry::emplace * @tparam Component Type of component to create. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace(Args &&... args) const { static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace(entt, std::forward(args)...); } /** * @brief Assigns or replaces the given component for a handle. * @sa basic_registry::emplace_or_replace * @tparam Component Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace_or_replace(Args &&... args) const { static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace_or_replace(entt, std::forward(args)...); } /** * @brief Patches the given component for a handle. * @sa basic_registry::patch * @tparam Component Type of component to patch. * @tparam Func Types of the function objects to invoke. * @param func Valid function objects. * @return A reference to the patched component. */ template decltype(auto) patch(Func &&... func) const { static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template patch(entt, std::forward(func)...); } /** * @brief Replaces the given component for a handle. * @sa basic_registry::replace * @tparam Component Type of component to replace. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ template decltype(auto) replace(Args &&... args) const { static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template replace(entt, std::forward(args)...); } /** * @brief Removes the given components from a handle. * @sa basic_registry::remove * @tparam Component Types of components to remove. * @return The number of components actually removed. */ template size_type remove() const { static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template remove(entt); } /** * @brief Erases the given components from a handle. * @sa basic_registry::erase * @tparam Component Types of components to erase. */ template void erase() const { static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); reg->template erase(entt); } /*! @copydoc remove */ template [[deprecated("Use ::remove instead")]] size_type remove_if_exists() const { return remove(); } /** * @brief Removes all the components from a handle and makes it orphaned. * @sa basic_registry::remove_all */ [[deprecated("No longer supported")]] void remove_all() const { static_assert(sizeof...(Type) == 0, "Invalid operation"); reg->remove_all(entt); } /** * @brief Checks if a handle has all the given components. * @sa basic_registry::all_of * @tparam Component Components for which to perform the check. * @return True if the handle has all the components, false otherwise. */ template [[nodiscard]] decltype(auto) all_of() const { return reg->template all_of(entt); } /** * @brief Checks if a handle has at least one of the given components. * @sa basic_registry::any_of * @tparam Component Components for which to perform the check. * @return True if the handle has at least one of the given components, * false otherwise. */ template [[nodiscard]] decltype(auto) any_of() const { return reg->template any_of(entt); } /** * @brief Returns references to the given components for a handle. * @sa basic_registry::get * @tparam Component Types of components to get. * @return References to the components owned by the handle. */ template [[nodiscard]] decltype(auto) get() const { static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template get(entt); } /** * @brief Returns a reference to the given component for a handle. * @sa basic_registry::get_or_emplace * @tparam Component Type of component to get. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the handle. */ template [[nodiscard]] decltype(auto) get_or_emplace(Args &&... args) const { static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template get_or_emplace(entt, std::forward(args)...); } /** * @brief Returns pointers to the given components for a handle. * @sa basic_registry::try_get * @tparam Component Types of components to get. * @return Pointers to the components owned by the handle. */ template [[nodiscard]] auto try_get() const { static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template try_get(entt); } /** * @brief Checks if a handle has components assigned. * @return True if the handle has no components assigned, false otherwise. */ [[nodiscard]] bool orphan() const { return reg->orphan(entt); } /** * @brief Visits a handle and returns the types for its components. * @sa basic_registry::visit * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void visit(Func&& func) const { reg->visit(entt, std::forward(func)); } private: registry_type* reg; entity_type entt; }; /** * @brief Compares two handles. * @tparam Type A valid entity type (see entt_traits for more details). * @tparam Other A valid entity type (see entt_traits for more details). * @param lhs A valid handle. * @param rhs A valid handle. * @return False if both handles refer to the same registry and the same * entity, true otherwise. */ template bool operator!=(const basic_handle& lhs, const basic_handle& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template basic_handle(basic_registry&, Entity) ->basic_handle; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template basic_handle(const basic_registry&, Entity) ->basic_handle; } #endif // #include "entity/helper.hpp" #ifndef ENTT_ENTITY_HELPER_HPP #define ENTT_ENTITY_HELPER_HPP #include // #include "../config/config.h" // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../core/type_traits.hpp" // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template auto function_pointer(Ret(*)(Args...))->Ret(*)(Args...); template auto function_pointer(Ret(*)(Type, Args...), Other&&)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...), Other &&...)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...) const, Other &&...)->Ret(*)(Args...); template auto function_pointer(Type Class::*, Other &&...)->Type(*)(); template using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { return std::index_sequence_for{}; } } /** * Internal details not to be documented. * @endcond */ /*! @brief Used to wrap a function or a member of a specified type. */ template struct connect_arg_t {}; /*! @brief Constant of type connect_arg_t used to disambiguate calls. */ template inline constexpr connect_arg_t connect_arg{}; /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void*, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type&, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type*, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void*, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() ENTT_NOEXCEPT : fn{ nullptr }, data{ nullptr } {} /** * @brief Constructs a delegate and connects a free function or an unbound * member. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ENTT_NOEXCEPT { connect(); } /** * @brief Constructs a delegate and connects a free function with payload or * a bound member. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template delegate(connect_arg_t, Type&& value_or_instance) ENTT_NOEXCEPT { connect(std::forward(value_or_instance)); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() ENTT_NOEXCEPT { data = nullptr; if constexpr (std::is_invocable_r_v) { fn = [](const void*, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr (std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type& value_or_instance) ENTT_NOEXCEPT { data = &value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type* value_or_instance) ENTT_NOEXCEPT { data = value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { fn = function; data = payload; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() ENTT_NOEXCEPT { fn = nullptr; data = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void* instance() const ENTT_NOEXCEPT { return data; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(data, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { // no need to test also data return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate& other) const ENTT_NOEXCEPT { return fn == other.fn && data == other.data; } private: function_type* fn; const void* data; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate& lhs, const delegate& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ->delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type&&) ->delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret(*)(const void*, Args...), const void* = nullptr) ->delegate; } #endif // #include "registry.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Converts a registry to a view. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct as_view { /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_view(registry_type& source) ENTT_NOEXCEPT: reg{ source } {} /** * @brief Conversion function from a registry to a view. * @tparam Exclude Types of components used to filter the view. * @tparam Component Type of components used to construct the view. * @return A newly created view. */ template operator basic_view() const { return reg.template view(Exclude{}); } private: registry_type& reg; }; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_view(basic_registry&)->as_view; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_view(const basic_registry&)->as_view; /** * @brief Converts a registry to a group. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct as_group { /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_group(registry_type& source) ENTT_NOEXCEPT: reg{ source } {} /** * @brief Conversion function from a registry to a group. * @tparam Exclude Types of components used to filter the group. * @tparam Get Types of components observed by the group. * @tparam Owned Types of components owned by the group. * @return A newly created group. */ template operator basic_group() const { if constexpr (std::is_const_v) { return reg.template group_if_exists(Get{}, Exclude{}); } else { return reg.template group(Get{}, Exclude{}); } } private: registry_type& reg; }; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_group(basic_registry&)->as_group; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_group(const basic_registry&)->as_group; /** * @brief Helper to create a listener that directly invokes a member function. * @tparam Member Member function to invoke on a component of the given type. * @tparam Entity A valid entity type (see entt_traits for more details). * @param reg A registry that contains the given entity and its components. * @param entt Entity from which to get the component. */ template void invoke(basic_registry& reg, const Entity entt) { static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); delegate&, const Entity)> func; func.template connect(reg.template get>(entt)); func(reg, entt); } /** * @brief Returns the entity associated with a given component. * * @warning * Currently, this function only works correctly with the default pool as it * makes assumptions about how the components are laid out. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Component Type of component. * @param reg A registry that contains the given entity and its components. * @param instance A valid component instance. * @return The entity associated with the given component. */ template Entity to_entity(const basic_registry& reg, const Component& instance) { const auto view = reg.template view(); const auto* addr = std::addressof(instance); for (auto it = view.rbegin(), last = view.rend(); it < last; it += ENTT_PACKED_PAGE) { if (const auto dist = (addr - std::addressof(view.template get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { return *(it + dist); } } return entt::null; } } #endif // #include "entity/observer.hpp" #ifndef ENTT_ENTITY_OBSERVER_HPP #define ENTT_ENTITY_OBSERVER_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" // #include "registry.hpp" // #include "storage.hpp" // #include "utility.hpp" // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /*! @brief Grouping matcher. */ template struct matcher {}; /** * @brief Collector. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template struct basic_collector; /** * @brief Collector. * * A collector contains a set of rules (literally, matchers) to use to track * entities.
* Its main purpose is to generate a descriptor that allows an observer to know * how to connect to a registry. */ template<> struct basic_collector<> { /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { return basic_collector, type_list<>, type_list, AllOf...>>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() ENTT_NOEXCEPT { return basic_collector, type_list<>, AnyOf>>{}; } }; /** * @brief Collector. * @copydetails basic_collector<> * @tparam Reject Untracked types used to filter out entities. * @tparam Require Untracked types required by the matcher. * @tparam Rule Specific details of the current matcher. * @tparam Other Other matchers. */ template struct basic_collector, type_list, Rule...>, Other...> { /*! @brief Current matcher. */ using current_type = matcher, type_list, Rule...>; /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() ENTT_NOEXCEPT { return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; } /** * @brief Updates the filter of the last added matcher. * @tparam AllOf Types of components required by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { using extended_type = matcher, type_list, Rule...>; return basic_collector{}; } }; /*! @brief Variable template used to ease the definition of collectors. */ inline constexpr basic_collector<> collector{}; /** * @brief Observer. * * An observer returns all the entities and only the entities that fit the * requirements of at least one matcher. Moreover, it's guaranteed that the * entity list is tightly packed in memory for fast iterations.
* In general, observers don't stay true to the order of any set of components. * * Observers work mainly with two types of matchers, provided through a * collector: * * * Observing matcher: an observer will return at least all the living entities * for which one or more of the given components have been updated and not yet * destroyed. * * Grouping matcher: an observer will return at least all the living entities * that would have entered the given group if it existed and that would have * not yet left it. * * If an entity respects the requirements of multiple matchers, it will be * returned once and only once by the observer in any case. * * Matchers support also filtering by means of a _where_ clause that accepts * both a list of types and an exclusion list.
* Whenever a matcher finds that an entity matches its requirements, the * condition of the filter is verified before to register the entity itself. * Moreover, a registered entity isn't returned by the observer if the condition * set by the filter is broken in the meantime. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @warning * Lifetime of an observer doesn't necessarily have to overcome that of the * registry to which it is connected. However, the observer must be disconnected * from the registry before being destroyed to avoid crashes due to dangling * pointers. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_observer { using payload_type = std::uint32_t; template struct matcher_handler; template struct matcher_handler, type_list, AnyOf>> { template static void maybe_valid_if(basic_observer& obs, basic_registry& reg, const Entity entt) { if (reg.template all_of(entt) && !reg.template any_of(entt)) { if (!obs.storage.contains(entt)) { obs.storage.emplace(entt); } obs.storage.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer& obs, basic_registry&, const Entity entt) { if (obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { obs.storage.erase(entt); } } template static void connect(basic_observer& obs, basic_registry& reg) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); reg.template on_update().template connect<&maybe_valid_if>(obs); reg.template on_destroy().template connect<&discard_if>(obs); } static void disconnect(basic_observer& obs, basic_registry& reg) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); reg.template on_update().disconnect(obs); reg.template on_destroy().disconnect(obs); } }; template struct matcher_handler, type_list, type_list, AllOf...>> { template static void maybe_valid_if(basic_observer& obs, basic_registry& reg, const Entity entt) { if ([®, entt]() { if constexpr (sizeof...(Ignore) == 0) { return reg.template all_of(entt) && !reg.template any_of(entt); } else { return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); } }()) { if (!obs.storage.contains(entt)) { obs.storage.emplace(entt); } obs.storage.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer& obs, basic_registry&, const Entity entt) { if (obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { obs.storage.erase(entt); } } template static void connect(basic_observer& obs, basic_registry& reg) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); } static void disconnect(basic_observer& obs, basic_registry& reg) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); } }; template static void disconnect(basic_registry& reg, basic_observer& obs) { (matcher_handler::disconnect(obs, reg), ...); } template void connect(basic_registry& reg, std::index_sequence) { static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); (matcher_handler::template connect(*this, reg), ...); release.template connect<&basic_observer::disconnect>(reg); } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_sparse_set::iterator; /*! @brief Default constructor. */ basic_observer() : release{}, storage{} {} /*! @brief Default copy constructor, deleted on purpose. */ basic_observer(const basic_observer&) = delete; /*! @brief Default move constructor, deleted on purpose. */ basic_observer(basic_observer&&) = delete; /** * @brief Creates an observer and connects it to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template basic_observer(basic_registry& reg, basic_collector) : basic_observer{} { connect(reg, std::index_sequence_for{}); } /*! @brief Default destructor. */ ~basic_observer() = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This observer. */ basic_observer& operator=(const basic_observer&) = delete; /** * @brief Default move assignment operator, deleted on purpose. * @return This observer. */ basic_observer& operator=(basic_observer&&) = delete; /** * @brief Connects an observer to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template void connect(basic_registry& reg, basic_collector) { disconnect(); connect(reg, std::index_sequence_for{}); storage.clear(); } /*! @brief Disconnects an observer from the registry it keeps track of. */ void disconnect() { if (release) { release(*this); release.reset(); } } /** * @brief Returns the number of elements in an observer. * @return Number of elements. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return storage.size(); } /** * @brief Checks whether an observer is empty. * @return True if the observer is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return storage.empty(); } /** * @brief Direct access to the list of entities of the observer. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @note * Entities are in the reverse order as returned by the `begin`/`end` * iterators. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type* data() const ENTT_NOEXCEPT { return storage.data(); } /** * @brief Returns an iterator to the first entity of the observer. * * The returned iterator points to the first entity of the observer. If the * container is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the observer. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return storage.basic_sparse_set::begin(); } /** * @brief Returns an iterator that is past the last entity of the observer. * * The returned iterator points to the entity following the last entity of * the observer. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * observer. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return storage.basic_sparse_set::end(); } /*! @brief Clears the underlying container. */ void clear() ENTT_NOEXCEPT { storage.clear(); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity.
* The signature of the function must be equivalent to the following form: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (const auto entity : *this) { func(entity); } } /** * @brief Iterates entities and applies the given function object to them, * then clears the observer. * * @sa each * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) { std::as_const(*this).each(std::move(func)); clear(); } private: delegate release; basic_storage storage; }; } #endif // #include "entity/organizer.hpp" #ifndef ENTT_ENTITY_ORGANIZER_HPP #define ENTT_ENTITY_ORGANIZER_HPP #include #include #include #include #include #include // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "fwd.hpp" // #include "helper.hpp" #ifndef ENTT_ENTITY_HELPER_HPP #define ENTT_ENTITY_HELPER_HPP #include // #include "../config/config.h" // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" // #include "registry.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Converts a registry to a view. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct as_view { /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_view(registry_type& source) ENTT_NOEXCEPT: reg{ source } {} /** * @brief Conversion function from a registry to a view. * @tparam Exclude Types of components used to filter the view. * @tparam Component Type of components used to construct the view. * @return A newly created view. */ template operator basic_view() const { return reg.template view(Exclude{}); } private: registry_type& reg; }; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_view(basic_registry&)->as_view; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_view(const basic_registry&)->as_view; /** * @brief Converts a registry to a group. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct as_group { /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /*! @brief Type of registry to convert. */ using registry_type = constness_as_t, Entity>; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_group(registry_type& source) ENTT_NOEXCEPT: reg{ source } {} /** * @brief Conversion function from a registry to a group. * @tparam Exclude Types of components used to filter the group. * @tparam Get Types of components observed by the group. * @tparam Owned Types of components owned by the group. * @return A newly created group. */ template operator basic_group() const { if constexpr (std::is_const_v) { return reg.template group_if_exists(Get{}, Exclude{}); } else { return reg.template group(Get{}, Exclude{}); } } private: registry_type& reg; }; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_group(basic_registry&)->as_group; /** * @brief Deduction guide. * @tparam Entity A valid entity type (see entt_traits for more details). */ template as_group(const basic_registry&)->as_group; /** * @brief Helper to create a listener that directly invokes a member function. * @tparam Member Member function to invoke on a component of the given type. * @tparam Entity A valid entity type (see entt_traits for more details). * @param reg A registry that contains the given entity and its components. * @param entt Entity from which to get the component. */ template void invoke(basic_registry& reg, const Entity entt) { static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); delegate&, const Entity)> func; func.template connect(reg.template get>(entt)); func(reg, entt); } /** * @brief Returns the entity associated with a given component. * * @warning * Currently, this function only works correctly with the default pool as it * makes assumptions about how the components are laid out. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Component Type of component. * @param reg A registry that contains the given entity and its components. * @param instance A valid component instance. * @return The entity associated with the given component. */ template Entity to_entity(const basic_registry& reg, const Component& instance) { const auto view = reg.template view(); const auto* addr = std::addressof(instance); for (auto it = view.rbegin(), last = view.rend(); it < last; it += ENTT_PACKED_PAGE) { if (const auto dist = (addr - std::addressof(view.template get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { return *(it + dist); } } return entt::null; } } #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct is_view : std::false_type {}; template struct is_view, Component...>> : std::true_type {}; template inline constexpr bool is_view_v = is_view::value; template struct unpack_type { using ro = std::conditional_t< type_list_contains_v> || (std::is_const_v && !type_list_contains_v>), type_list>, type_list<> >; using rw = std::conditional_t< type_list_contains_v> || (!std::is_const_v && !type_list_contains_v>), type_list, type_list<> >; }; template struct unpack_type, type_list> { using ro = type_list<>; using rw = type_list<>; }; template struct unpack_type, type_list> : unpack_type, type_list> {}; template struct unpack_type, Component...>, type_list> { using ro = type_list_cat_t, typename unpack_type>::ro...>; using rw = type_list_cat_t>::rw...>; }; template struct unpack_type, Component...>, type_list> : unpack_type, Component...>, type_list> {}; template struct resource; template struct resource, type_list> { using args = type_list...>; using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; }; template resource...>, type_list> free_function_to_resource(Ret(*)(Args...)); template resource...>, type_list> constrained_function_to_resource(Ret(*)(Type&, Args...)); template resource...>, type_list> constrained_function_to_resource(Ret(Class::*)(Args...)); template resource...>, type_list> constrained_function_to_resource(Ret(Class::*)(Args...) const); template resource, type_list> to_resource(); } /** * Internal details not to be documented. * @endcond */ /** * @brief Utility class for creating a static task graph. * * This class offers minimal support (but sufficient in many cases) for creating * an execution graph from functions and their requirements on resources.
* Note that the resulting tasks aren't executed in any case. This isn't the * goal of the tool. Instead, they are returned to the user in the form of a * graph that allows for safe execution. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_organizer final { using callback_type = void(const void*, entt::basic_registry&); using prepare_type = void(entt::basic_registry&); using dependency_type = std::size_t(const bool, type_info*, const std::size_t); struct vertex_data final { std::size_t ro_count{}; std::size_t rw_count{}; const char* name{}; const void* payload{}; callback_type* callback{}; dependency_type* dependency; prepare_type* prepare{}; type_info info{}; }; template [[nodiscard]] static decltype(auto) extract(basic_registry& reg) { if constexpr (std::is_same_v>) { return reg; } else if constexpr (internal::is_view_v) { return as_view{ reg }; } else { return reg.template ctx_or_set>(); } } template [[nodiscard]] static auto to_args(basic_registry& reg, type_list) { return std::tuple(reg))...>(extract(reg)...); } template static std::size_t fill_dependencies(type_list, [[maybe_unused]] type_info* buffer, [[maybe_unused]] const std::size_t count) { if constexpr (sizeof...(Type) == 0u) { return {}; } else { type_info info[sizeof...(Type)]{ type_id()... }; const auto length = (std::min)(count, sizeof...(Type)); std::copy_n(info, length, buffer); return length; } } template void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { dependencies[type_hash>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); (dependencies[type_hash::value()].emplace_back(index, false), ...); (dependencies[type_hash::value()].emplace_back(index, true), ...); } [[nodiscard]] std::vector adjacency_matrix() { const auto length = vertices.size(); std::vector edges(length * length, false); // creates the ajacency matrix for (const auto& deps : dependencies) { const auto last = deps.second.cend(); auto it = deps.second.cbegin(); while (it != last) { if (it->second) { // rw item if (auto curr = it++; it != last) { if (it->second) { edges[curr->first * length + it->first] = true; } else { if (const auto next = std::find_if(it, last, [](const auto& elem) { return elem.second; }); next != last) { for (; it != next; ++it) { edges[curr->first * length + it->first] = true; edges[it->first * length + next->first] = true; } } else { for (; it != next; ++it) { edges[curr->first * length + it->first] = true; } } } } } else { // ro item, possibly only on first iteration if (const auto next = std::find_if(it, last, [](const auto& elem) { return elem.second; }); next != last) { for (; it != next; ++it) { edges[it->first * length + next->first] = true; } } else { it = last; } } } } // computes the transitive closure for (std::size_t vk{}; vk < length; ++vk) { for (std::size_t vi{}; vi < length; ++vi) { for (std::size_t vj{}; vj < length; ++vj) { edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]); } } } // applies the transitive reduction for (std::size_t vert{}; vert < length; ++vert) { edges[vert * length + vert] = false; } for (std::size_t vj{}; vj < length; ++vj) { for (std::size_t vi{}; vi < length; ++vi) { if (edges[vi * length + vj]) { for (std::size_t vk{}; vk < length; ++vk) { if (edges[vj * length + vk]) { edges[vi * length + vk] = false; } } } } } return edges; } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Raw task function type. */ using function_type = callback_type; /*! @brief Vertex type of a task graph defined as an adjacency list. */ struct vertex { /** * @brief Constructs a vertex of the task graph. * @param vtype True if the vertex is a top-level one, false otherwise. * @param data The data associated with the vertex. * @param edges The indices of the children in the adjacency list. */ vertex(const bool vtype, vertex_data data, std::vector edges) : is_top_level{ vtype }, node{ std::move(data) }, reachable{ std::move(edges) } {} /** * @brief Fills a buffer with the type info objects for the writable * resources of a vertex. * @param buffer A buffer pre-allocated by the user. * @param length The length of the user-supplied buffer. * @return The number of type info objects written to the buffer. */ size_type ro_dependency(type_info* buffer, const std::size_t length) const ENTT_NOEXCEPT { return node.dependency(false, buffer, length); } /** * @brief Fills a buffer with the type info objects for the read-only * resources of a vertex. * @param buffer A buffer pre-allocated by the user. * @param length The length of the user-supplied buffer. * @return The number of type info objects written to the buffer. */ size_type rw_dependency(type_info* buffer, const std::size_t length) const ENTT_NOEXCEPT { return node.dependency(true, buffer, length); } /** * @brief Returns the number of read-only resources of a vertex. * @return The number of read-only resources of the vertex. */ size_type ro_count() const ENTT_NOEXCEPT { return node.ro_count; } /** * @brief Returns the number of writable resources of a vertex. * @return The number of writable resources of the vertex. */ size_type rw_count() const ENTT_NOEXCEPT { return node.rw_count; } /** * @brief Checks if a vertex is also a top-level one. * @return True if the vertex is a top-level one, false otherwise. */ bool top_level() const ENTT_NOEXCEPT { return is_top_level; } /** * @brief Returns a type info object associated with a vertex. * @return A properly initialized type info object. */ type_info info() const ENTT_NOEXCEPT { return node.info; } /** * @brief Returns a user defined name associated with a vertex, if any. * @return The user defined name associated with the vertex, if any. */ const char* name() const ENTT_NOEXCEPT { return node.name; } /** * @brief Returns the function associated with a vertex. * @return The function associated with the vertex. */ function_type* callback() const ENTT_NOEXCEPT { return node.callback; } /** * @brief Returns the payload associated with a vertex, if any. * @return The payload associated with the vertex, if any. */ const void* data() const ENTT_NOEXCEPT { return node.payload; } /** * @brief Returns the list of nodes reachable from a given vertex. * @return The list of nodes reachable from the vertex. */ const std::vector& children() const ENTT_NOEXCEPT { return reachable; } /** * @brief Prepares a registry and assures that all required resources * are properly instantiated before using them. * @param reg A valid registry. */ void prepare(basic_registry& reg) const { node.prepare ? node.prepare(reg) : void(); } private: bool is_top_level; vertex_data node; std::vector reachable; }; /** * @brief Adds a free function to the task list. * @tparam Candidate Function to add to the task list. * @tparam Req Additional requirements and/or override resource access mode. * @param name Optional name to associate with the task. */ template void emplace(const char* name = nullptr) { using resource_type = decltype(internal::free_function_to_resource(Candidate)); constexpr auto requires_registry = type_list_contains_v>; callback_type* callback = +[](const void*, basic_registry& reg) { std::apply(Candidate, to_args(reg, typename resource_type::args{})); }; track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); vertices.push_back({ resource_type::ro::size, resource_type::rw::size, name, nullptr, callback, +[](const bool rw, type_info* buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, +[](basic_registry& reg) { void(to_args(reg, typename resource_type::args{})); }, type_id>() }); } /** * @brief Adds a free function with payload or a member function with an * instance to the task list. * @tparam Candidate Function or member to add to the task list. * @tparam Req Additional requirements and/or override resource access mode. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @param name Optional name to associate with the task. */ template void emplace(Type& value_or_instance, const char* name = nullptr) { using resource_type = decltype(internal::constrained_function_to_resource(Candidate)); constexpr auto requires_registry = type_list_contains_v>; callback_type* callback = +[](const void* payload, basic_registry& reg) { Type* curr = static_cast(const_cast *>(payload)); std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); }; track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); vertices.push_back({ resource_type::ro::size, resource_type::rw::size, name, &value_or_instance, callback, +[](const bool rw, type_info* buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, +[](basic_registry& reg) { void(to_args(reg, typename resource_type::args{})); }, type_id>() }); } /** * @brief Adds an user defined function with optional payload to the task * list. * @tparam Req Additional requirements and/or override resource access mode. * @param func Function to add to the task list. * @param payload User defined arbitrary data. * @param name Optional name to associate with the task. */ template void emplace(function_type* func, const void* payload = nullptr, const char* name = nullptr) { using resource_type = internal::resource, type_list>; track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); vertices.push_back({ resource_type::ro::size, resource_type::rw::size, name, payload, func, +[](const bool rw, type_info* buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, nullptr, type_info{} }); } /** * @brief Generates a task graph for the current content. * @return The adjacency list of the task graph. */ std::vector graph() { const auto edges = adjacency_matrix(); // creates the adjacency list std::vector adjacency_list{}; adjacency_list.reserve(vertices.size()); for (std::size_t col{}, length = vertices.size(); col < length; ++col) { std::vector reachable{}; const auto row = col * length; bool is_top_level = true; for (std::size_t next{}; next < length; ++next) { if (edges[row + next]) { reachable.push_back(next); } } for (std::size_t next{}; next < length && is_top_level; ++next) { is_top_level = !edges[next * length + col]; } adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable)); } return adjacency_list; } /*! @brief Erases all elements from a container. */ void clear() { dependencies.clear(); vertices.clear(); } private: std::unordered_map>> dependencies; std::vector vertices; }; } #endif // #include "entity/poly_storage.hpp" #ifndef ENTT_ENTITY_POLY_STORAGE_HPP #define ENTT_ENTITY_POLY_STORAGE_HPP #include #include // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../poly/poly.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Basic poly storage implementation. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct Storage : type_list { /** * @brief Concept definition. * @tparam Base Opaque base class from which to inherit. */ template struct type : Base { /** * @brief Returns a type info for the contained objects. * @return The type info for the contained objects. */ type_info value_type() const ENTT_NOEXCEPT { return poly_call<0>(*this); } }; /** * @brief Concept implementation. * @tparam Type Type for which to generate an implementation. */ template using impl = value_list<&type_id>; }; /** * @brief Defines the poly storage type associate with a given entity type. * @tparam Entity A valid entity type (see entt_traits for more details). */ template struct poly_storage_traits { /*! @brief Poly storage type for the given entity type. */ using storage_type = poly>; }; } #endif // #include "entity/registry.hpp" #ifndef ENTT_ENTITY_REGISTRY_HPP #define ENTT_ENTITY_REGISTRY_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/any.hpp" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "group.hpp" // #include "poly_storage.hpp" // #include "runtime_view.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" // #include "utility.hpp" // #include "view.hpp" namespace entt { /** * @brief Fast and reliable entity-component system. * * The registry is the core class of the entity-component framework.
* It stores entities and arranges pools of components on a per request basis. * By means of a registry, users can manage entities and components, then create * views or groups to iterate them. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_registry { using traits_type = entt_traits; using poly_storage_type = typename poly_storage_traits::storage_type; using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Component>; struct pool_data { poly_storage_type poly; std::unique_ptr pool{}; }; template struct group_handler; template struct group_handler, get_t, Owned...> { static_assert(!std::disjunction_v::in_place_delete...>, "Groups do not support in-place delete"); static_assert(std::conjunction_v>..., std::is_same>..., std::is_same>...>, "One or more component types are invalid"); std::conditional_t current{}; template void maybe_valid_if(basic_registry& owner, const Entity entt) { [[maybe_unused]] const auto cpools = std::make_tuple(owner.assure()...); const auto is_valid = ((std::is_same_v || std::get*>(cpools)->contains(entt)) && ...) && ((std::is_same_v || owner.assure()->contains(entt)) && ...) && ((std::is_same_v || !owner.assure()->contains(entt)) && ...); if constexpr (sizeof...(Owned) == 0) { if (is_valid && !current.contains(entt)) { current.emplace(entt); } } else { if (is_valid && !(std::get<0>(cpools)->index(entt) < current)) { const auto pos = current++; (std::get*>(cpools)->swap(std::get*>(cpools)->data()[pos], entt), ...); } } } void discard_if([[maybe_unused]] basic_registry& owner, const Entity entt) { if constexpr (sizeof...(Owned) == 0) { current.remove(entt); } else { if (const auto cpools = std::make_tuple(owner.assure()...); std::get<0>(cpools)->contains(entt) && (std::get<0>(cpools)->index(entt) < current)) { const auto pos = --current; (std::get*>(cpools)->swap(std::get*>(cpools)->data()[pos], entt), ...); } } } }; struct group_data { std::size_t size; std::unique_ptr group; bool (*owned)(const id_type) ENTT_NOEXCEPT; bool (*get)(const id_type) ENTT_NOEXCEPT; bool (*exclude)(const id_type) ENTT_NOEXCEPT; }; template [[nodiscard]] storage_type* assure() const { static_assert(std::is_same_v>, "Non-decayed types not allowed"); const auto index = type_seq::value(); if (!(index < pools.size())) { pools.resize(size_type(index) + 1u); } if (auto&& pdata = pools[index]; !pdata.pool) { pdata.pool.reset(new storage_type()); pdata.poly.template emplace&>(*static_cast *>(pdata.pool.get())); } return static_cast *>(pools[index].pool.get()); } template [[nodiscard]] const storage_type* pool_if_exists() const ENTT_NOEXCEPT { static_assert(std::is_same_v>, "Non-decayed types not allowed"); const auto index = type_seq::value(); return (!(index < pools.size()) || !pools[index].pool) ? nullptr : static_cast *>(pools[index].pool.get()); } auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { ENTT_ASSERT(pos < traits_type::to_integral(null), "No entities available"); return traits_type::construct(static_cast(pos), {}); } auto recycle_identifier() ENTT_NOEXCEPT { ENTT_ASSERT(free_list != null, "No entities available"); const auto curr = traits_type::to_entity(free_list); free_list = (tombstone | entities[curr]); return (entities[curr] = traits_type::construct(curr, traits_type::to_version(entities[curr]))); } auto release_entity(const Entity entity, const typename traits_type::version_type version) { const typename traits_type::version_type vers = version + (version == traits_type::to_version(tombstone)); entities[traits_type::to_entity(entity)] = traits_type::construct(traits_type::to_entity(free_list), vers); free_list = (tombstone | entity); return vers; } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Underlying version type. */ using version_type = typename traits_type::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Poly storage type. */ using poly_storage = typename poly_storage_traits::storage_type; /** * @brief Returns the entity identifier without the version. * @param entity An entity identifier, either valid or not. * @return The entity identifier without the version. */ [[nodiscard]] static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { return traits_type::construct(traits_type::to_entity(entity), {}); } /** * @brief Returns the version stored along with an entity identifier. * @param entity An entity identifier, either valid or not. * @return The version stored along with the given entity identifier. */ [[nodiscard]] static version_type version(const entity_type entity) ENTT_NOEXCEPT { return traits_type::to_version(entity); } /*! @brief Default constructor. */ basic_registry() = default; /*! @brief Default move constructor. */ basic_registry(basic_registry&&) = default; /*! @brief Default move assignment operator. @return This registry. */ basic_registry& operator=(basic_registry&&) = default; /** * @brief Prepares a pool for the given type if required. * @tparam Component Type of component for which to prepare a pool. */ template void prepare() { // suppress the warning due to the [[nodiscard]] attribute static_cast(assure()); } /** * @brief Returns a poly storage for a given type. * @param info The type for which to return a poly storage. * @return A valid poly storage if a pool for the given type exists, an * empty and thus invalid element otherwise. */ poly_storage& storage(const type_info info) { ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly, "Storage not available"); return pools[info.seq()].poly; } /*! @copydoc storage */ const poly_storage& storage(const type_info info) const { ENTT_ASSERT(info.seq() < pools.size() && pools[info.seq()].poly, "Storage not available"); return pools[info.seq()].poly; } /** * @brief Returns the number of existing components of the given type. * @tparam Component Type of component of which to return the size. * @return Number of existing components of the given type. */ template [[nodiscard]] size_type size() const { const auto* cpool = pool_if_exists(); return cpool ? cpool->size() : size_type{}; } /** * @brief Returns the number of entities created so far. * @return Number of entities created so far. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return entities.size(); } /** * @brief Returns the number of entities still in use. * @return Number of entities still in use. */ [[nodiscard]] size_type alive() const { auto sz = entities.size(); for (auto curr = free_list; curr != null; --sz) { curr = entities[traits_type::to_entity(curr)]; } return sz; } /** * @brief Increases the capacity of the registry or of the pools for the * given components. * * If no components are specified, the capacity of the registry is * increased, that is the number of entities it contains. Otherwise the * capacity of the pools for the given components is increased.
* In both cases, if the new capacity is greater than the current capacity, * new storage is allocated, otherwise the method does nothing. * * @tparam Component Types of components for which to reserve storage. * @param cap Desired capacity. */ template void reserve(const size_type cap) { if constexpr (sizeof...(Component) == 0) { entities.reserve(cap); } else { (assure()->reserve(cap), ...); } } /** * @brief Reserves enough space to store `count` pools. * @param count Number of pools to reserve space for. */ [[deprecated("No longer supported")]] void reserve_pools(const size_t count) { pools.reserve(count); } /** * @brief Returns the capacity of the pool for the given component. * @tparam Component Type of component in which one is interested. * @return Capacity of the pool of the given component. */ template [[nodiscard]] size_type capacity() const { const auto* cpool = pool_if_exists(); return cpool ? cpool->capacity() : size_type{}; } /** * @brief Returns the number of entities that a registry has currently * allocated space for. * @return Capacity of the registry. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return entities.capacity(); } /** * @brief Requests the removal of unused capacity for the given components. * @tparam Component Types of components for which to reclaim unused * capacity. */ template void shrink_to_fit() { (assure()->shrink_to_fit(), ...); } /** * @brief Checks whether the registry or the pools of the given components * are empty. * * A registry is considered empty when it doesn't contain entities that are * still in use. * * @tparam Component Types of components in which one is interested. * @return True if the registry or the pools of the given components are * empty, false otherwise. */ template [[nodiscard]] bool empty() const { if constexpr (sizeof...(Component) == 0) { return !alive(); } else { return [](const auto *... cpool) { return ((!cpool || cpool->empty()) && ...); }(pool_if_exists()...); } } /** * @brief Direct access to the list of entities of a registry. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @warning * This list contains both valid and destroyed entities and isn't suitable * for direct use. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type* data() const ENTT_NOEXCEPT { return entities.data(); } /** * @brief Returns the head of the list of released entities. * * This function is intended for use in conjunction with `assign`.
* The returned entity has an invalid identifier in all cases. * * @return The head of the list of released entities. */ [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { return free_list; } /*! @copydoc released */ [[deprecated("Use ::released instead")]] [[nodiscard]] entity_type destroyed() const ENTT_NOEXCEPT { return released(); } /** * @brief Checks if an entity identifier refers to a valid entity. * @param entity An entity identifier, either valid or not. * @return True if the identifier is valid, false otherwise. */ [[nodiscard]] bool valid(const entity_type entity) const { const auto pos = size_type(traits_type::to_entity(entity)); return (pos < entities.size() && entities[pos] == entity); } /** * @brief Returns the actual version for an entity identifier. * * @warning * Attempting to use an entity that doesn't belong to the registry results * in undefined behavior. An entity belongs to the registry even if it has * been previously destroyed and/or recycled. * * @param entity A valid entity identifier. * @return Actual version for the given entity identifier. */ [[nodiscard]] version_type current(const entity_type entity) const { const auto pos = size_type(traits_type::to_entity(entity)); ENTT_ASSERT(pos < entities.size(), "Entity does not exist"); return version(entities[pos]); } /** * @brief Creates a new entity and returns it. * * There are two kinds of possible entity identifiers: * * * Newly created ones in case no entities have been previously destroyed. * * Recycled ones with updated versions. * * @return A valid entity identifier. */ [[nodiscard]] entity_type create() { return (free_list == null) ? entities.emplace_back(generate_identifier(entities.size())) : recycle_identifier(); } /** * @brief Creates a new entity and returns it. * * @sa create * * If the requested entity isn't in use, the suggested identifier is created * and returned. Otherwise, a new identifier is generated. * * @param hint Required entity identifier. * @return A valid entity identifier. */ [[nodiscard]] entity_type create(const entity_type hint) { const auto length = entities.size(); if (hint == null || hint == tombstone) { return create(); } else if (const auto req = traits_type::to_entity(hint); !(req < length)) { entities.resize(size_type(req) + 1u, null); for (auto pos = length; pos < req; ++pos) { release_entity(generate_identifier(pos), {}); } return (entities[req] = hint); } else if (const auto curr = traits_type::to_entity(entities[req]); req == curr) { return create(); } else { auto* it = &free_list; for (; traits_type::to_entity(*it) != req; it = &entities[traits_type::to_entity(*it)]); *it = traits_type::construct(curr, traits_type::to_version(*it)); return (entities[req] = hint); } } /** * @brief Assigns each element in a range an entity. * * @sa create * * @tparam It Type of forward iterator. * @param first An iterator to the first element of the range to generate. * @param last An iterator past the last element of the range to generate. */ template void create(It first, It last) { for (; free_list != null && first != last; ++first) { *first = recycle_identifier(); } const auto length = entities.size(); entities.resize(length + std::distance(first, last), null); for (auto pos = length; first != last; ++first, ++pos) { *first = entities[pos] = generate_identifier(pos); } } /** * @brief Assigns entities to an empty registry. * * This function is intended for use in conjunction with `data`, `size` and * `destroyed`.
* Don't try to inject ranges of randomly generated entities nor the _wrong_ * head for the list of destroyed entities. There is no guarantee that a * registry will continue to work properly in this case. * * @warning * There must be no entities still alive for this to work properly. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param destroyed The head of the list of destroyed entities. */ template void assign(It first, It last, const entity_type destroyed) { ENTT_ASSERT(!alive(), "Entities still alive"); entities.assign(first, last); free_list = destroyed; } /** * @brief Releases an entity identifier. * * The version is updated and the identifier can be recycled at any time. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. * @return The version of the recycled entity. */ version_type release(const entity_type entity) { return release(entity, version(entity) + 1u); } /** * @brief Releases an entity identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa release * * @param entity A valid entity identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type release(const entity_type entity, const version_type version) { ENTT_ASSERT(orphan(entity), "Non-orphan entity"); return release_entity(entity, version); } /** * @brief Releases all entity identifiers in a range. * * @sa release * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void release(It first, It last) { for (; first != last; ++first) { release(*first, version(*first) + 1u); } } /** * @brief Destroys an entity and releases its identifier. * * The version is updated and the identifier can be recycled at any time. * * @warning * Adding or removing components to an entity that is being destroyed can * result in undefined behavior. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. * @return The version of the recycled entity. */ version_type destroy(const entity_type entity) { return destroy(entity, version(entity) + 1u); } /** * @brief Destroys an entity and releases its identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa destroy * * @param entity A valid entity identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type destroy(const entity_type entity, const version_type version) { ENTT_ASSERT(valid(entity), "Invalid entity"); for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(entity, this); } return release_entity(entity, version); } /** * @brief Destroys all entities in a range and releases their identifiers. * * @sa destroy * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void destroy(It first, It last) { if constexpr (is_iterator_type_v) { for (; first != last; ++first) { destroy(*first, version(*first) + 1u); } } else { for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(first, last, this); } release(first, last); } } /** * @brief Assigns the given component to an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of * aggregate type). Then the component is assigned to the given entity. * * @warning * Attempting to use an invalid entity or to assign a component to an entity * that already owns it results in undefined behavior. * * @tparam Component Type of component to create. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); return assure()->emplace(*this, entity, std::forward(args)...); } /** * @brief Assigns each entity in a range the given component. * * @sa emplace * * @tparam Component Type of component to create. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the component to assign. */ template void insert(It first, It last, const Component& value = {}) { ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); assure()->insert(*this, first, last, value); } /** * @brief Assigns each entity in a range the given components. * * @sa emplace * * @tparam Component Type of component to create. * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of components. */ template::value_type>, Component>>> void insert(EIt first, EIt last, CIt from) { static_assert(std::is_constructible_v::value_type>, "Invalid value type"); ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); assure()->insert(*this, first, last, from); } /** * @brief Assigns or replaces the given component for an entity. * * Equivalent to the following snippet (pseudocode): * * @code{.cpp} * auto &component = registry.all_of(entity) ? registry.replace(entity, args...) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace_or_replace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); auto* cpool = assure(); return cpool->contains(entity) ? cpool->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{ std::forward(args)... }), ...); }) : cpool->emplace(*this, entity, std::forward(args)...); } /** * @brief Patches the given component for an entity. * * The signature of the functions should be equivalent to the following: * * @code{.cpp} * void(Component &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned. However, this function can be used to trigger an update signal * for them. * * @warning * Attempting to use an invalid entity or to patch a component of an entity * that doesn't own it results in undefined behavior. * * @tparam Component Type of component to patch. * @tparam Func Types of the function objects to invoke. * @param entity A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched component. */ template decltype(auto) patch(const entity_type entity, Func &&... func) { ENTT_ASSERT(valid(entity), "Invalid entity"); return assure()->patch(*this, entity, std::forward(func)...); } /** * @brief Replaces the given component for an entity. * * A new instance of the given component is created and initialized with the * arguments provided (the component must have a proper constructor or be of * aggregate type). Then the component is assigned to the given entity. * * @warning * Attempting to use an invalid entity or to replace a component of an * entity that doesn't own it results in undefined behavior. * * @tparam Component Type of component to replace. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ template decltype(auto) replace(const entity_type entity, Args &&... args) { return assure()->patch(*this, entity, [&args...](auto &... curr) { ((curr = Component{ std::forward(args)... }), ...); }); } /** * @brief Removes the given components from an entity. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Types of components to remove. * @param entity A valid entity identifier. * @return The number of components actually removed. */ template size_type remove(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); return (assure()->remove(entity, this) + ... + size_type{}); } /** * @brief Removes the given components from all the entities in a range. * * @sa remove * * @tparam Component Types of components to remove. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return The number of components actually removed. */ template size_type remove(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); const auto cpools = std::make_tuple(assure()...); size_type count{}; for (; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); count += (std::get*>(cpools)->remove(entity, this) + ...); } return count; } /** * @brief Erases the given components from an entity. * * @warning * Attempting to use an invalid entity or to erase a component from an * entity that doesn't own it results in undefined behavior. * * @tparam Component Types of components to erase. * @param entity A valid entity identifier. */ template void erase(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); static_assert(sizeof...(Component) > 0, "Provide one or more component types"); (assure()->erase(entity, this), ...); } /** * @brief Erases the given components from all the entities in a range. * * @sa erase * * @tparam Component Types of components to erase. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void erase(It first, It last) { static_assert(sizeof...(Component) > 0, "Provide one or more component types"); const auto cpools = std::make_tuple(assure()...); for (; first != last; ++first) { const auto entity = *first; ENTT_ASSERT(valid(entity), "Invalid entity"); (std::get*>(cpools)->erase(entity, this), ...); } } /** * @brief Removes all tombstones from a registry or only the pools for the * given components. * @tparam Component Types of components for which to clear all tombstones. */ template void compact() { if constexpr (sizeof...(Component) == 0) { for (auto&& pdata : pools) { pdata.pool && (pdata.pool->compact(), true); } } else { (assure()->compact(), ...); } } /*! @copydoc remove */ template [[deprecated("Use ::remove instead")]] size_type remove_if_exists(const entity_type entity) { return remove(entity); } /** * @brief Removes all the components from an entity and makes it orphaned. * * @warning * In case there are listeners that observe the destruction of components * and assign other components to the entity in their bodies, the result of * invoking this function may not be as expected. In the worst case, it * could lead to undefined behavior. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entity A valid entity identifier. */ [[deprecated("Use ::destroy(entity)/::create(entity) instead")]] void remove_all(const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); for (auto&& pdata : pools) { pdata.pool&& pdata.pool->remove(entity, this); } } /** * @brief Checks if an entity has all the given components. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. * @return True if the entity has all the components, false otherwise. */ template [[nodiscard]] bool all_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return [entity](const auto *... cpool) { return ((cpool && cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** * @brief Checks if an entity has at least one of the given components. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Components for which to perform the check. * @param entity A valid entity identifier. * @return True if the entity has at least one of the given components, * false otherwise. */ template [[nodiscard]] bool any_of(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return [entity](const auto *... cpool) { return !((!cpool || !cpool->contains(entity)) && ...); }(pool_if_exists()...); } /** * @brief Returns references to the given components for an entity. * * @warning * Attempting to use an invalid entity or to get a component from an entity * that doesn't own it results in undefined behavior. * * @tparam Component Types of components to get. * @param entity A valid entity identifier. * @return References to the components owned by the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { const auto* cpool = pool_if_exists...>(); ENTT_ASSERT(cpool, "Storage not available"); return cpool->get(entity); } else { return std::forward_as_tuple(get(entity)...); } } /*! @copydoc get */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { return (const_cast(assure>()->get(entity)), ...); } else { return std::forward_as_tuple(get(entity)...); } } /** * @brief Returns a reference to the given component for an entity. * * In case the entity doesn't own the component, the parameters provided are * used to construct it.
* Equivalent to the following snippet (pseudocode): * * @code{.cpp} * auto &component = registry.all_of(entity) ? registry.get(entity) : registry.emplace(entity, args...); * @endcode * * Prefer this function anyway because it has slightly better performance. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @tparam Component Type of component to get. * @tparam Args Types of arguments to use to construct the component. * @param entity A valid entity identifier. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the entity. */ template [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity), "Invalid entity"); auto* cpool = assure(); return cpool->contains(entity) ? cpool->get(entity) : cpool->emplace(*this, entity, std::forward(args)...); } /** * @brief Returns pointers to the given components for an entity. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @note * The registry retains ownership of the pointed-to components. * * @tparam Component Types of components to get. * @param entity A valid entity identifier. * @return Pointers to the components owned by the entity. */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { const auto* cpool = pool_if_exists...>(); return (cpool && cpool->contains(entity)) ? &cpool->get(entity) : nullptr; } else { return std::make_tuple(try_get(entity)...); } } /*! @copydoc try_get */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { ENTT_ASSERT(valid(entity), "Invalid entity"); if constexpr (sizeof...(Component) == 1) { return (const_cast(std::as_const(*this).template try_get(entity)), ...); } else { return std::make_tuple(try_get(entity)...); } } /** * @brief Clears a whole registry or the pools for the given components. * @tparam Component Types of components to remove from their entities. */ template void clear() { if constexpr (sizeof...(Component) == 0) { for (auto&& pdata : pools) { pdata.pool && (pdata.pool->clear(this), true); } each([this](const auto entity) { release_entity(entity, version(entity) + 1u); }); } else { (assure()->clear(this), ...); } } /** * @brief Iterates all the entities that are still in use. * * The function object is invoked for each entity that is still in use.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const Entity); * @endcode * * This function is fairly slow and should not be used frequently. However, * it's useful for iterating all the entities still in use, regardless of * their components. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if (free_list == null) { for (auto pos = entities.size(); pos; --pos) { func(entities[pos - 1]); } } else { for (auto pos = entities.size(); pos; --pos) { if (const auto entity = entities[pos - 1]; traits_type::to_entity(entity) == (pos - 1)) { func(entity); } } } } /** * @brief Checks if an entity has components assigned. * @param entity A valid entity identifier. * @return True if the entity has no components assigned, false otherwise. */ [[nodiscard]] bool orphan(const entity_type entity) const { ENTT_ASSERT(valid(entity), "Invalid entity"); return std::none_of(pools.cbegin(), pools.cend(), [entity](auto&& pdata) { return pdata.pool && pdata.pool->contains(entity); }); } /** * @brief Iterates orphans and applies them the given function object. * * The function object is invoked for each entity that is still in use and * has no components assigned.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const Entity); * @endcode * * This function can be very slow and should not be used frequently. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void orphans(Func func) const { each([this, &func](const auto entity) { if (orphan(entity)) { func(entity); } }); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever a new instance of the given component is created and assigned to * an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been assigned to the * entity. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_construct() { return assure()->on_construct(); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever an instance of the given component is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** the component has been updated. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_update() { return assure()->on_update(); } /** * @brief Returns a sink object for the given component. * * The sink returned by this function can be used to receive notifications * whenever an instance of the given component is removed from an entity and * thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **before** the component has been removed from the * entity. * * @sa sink * * @tparam Component Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_destroy() { return assure()->on_destroy(); } /** * @brief Returns a view for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Feel free to discard a view after the use. Creating and destroying a view * is an incredibly cheap operation because they do not require any type of * initialization.
* As a rule of thumb, storing a view should never be an option. * * Views do their best to iterate the smallest set of candidate entities. * In particular: * * * Single component views are incredibly fast and iterate a packed array * of entities, all of which has the given component. * * Multi component views look at the number of entities available for each * component and pick up a reference to the smallest set of candidates to * test for the given components. * * Views in no way affect the functionalities of the registry nor those of * the underlying pools. * * @note * Multi component views are pretty fast. However their performance tend to * degenerate when the number of components to iterate grows up and the most * of the entities have all the given components.
* To get a performance boost, consider using a group instead. * * @tparam Component Type of components used to construct the view. * @tparam Exclude Types of components used to filter the view. * @return A newly created view. */ template [[nodiscard]] basic_view, std::add_const_t...> view(exclude_t = {}) const { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); return { *assure>()..., *assure()... }; } /*! @copydoc view */ template [[nodiscard]] basic_view, Component...> view(exclude_t = {}) { static_assert(sizeof...(Component) > 0, "Exclusion-only views are not supported"); return { *assure>()..., *assure()... }; } /** * @brief Returns a runtime view for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Users should throw away the view after use. Fortunately, creating and * destroying a runtime view is an incredibly cheap operation because they * do not require any type of initialization.
* As a rule of thumb, storing a view should never be an option. * * Runtime views are to be used when users want to construct a view from * some external inputs and don't know at compile-time what are the required * components. * * @tparam ItComp Type of input iterator for the components to use to * construct the view. * @tparam ItExcl Type of input iterator for the components to use to filter * the view. * @param first An iterator to the first element of the range of components * to use to construct the view. * @param last An iterator past the last element of the range of components * to use to construct the view. * @param from An iterator to the first element of the range of components * to use to filter the view. * @param to An iterator past the last element of the range of components to * use to filter the view. * @return A newly created runtime view. */ template [[nodiscard]] basic_runtime_view runtime_view(ItComp first, ItComp last, ItExcl from = {}, ItExcl to = {}) const { std::vector component(std::distance(first, last)); std::vector filter(std::distance(from, to)); std::transform(first, last, component.begin(), [this](const auto ctype) { const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto&& pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); std::transform(from, to, filter.begin(), [this](const auto ctype) { const auto it = std::find_if(pools.cbegin(), pools.cend(), [ctype](auto&& pdata) { return pdata.poly && pdata.poly->value_type().hash() == ctype; }); return it == pools.cend() ? nullptr : it->pool.get(); }); return { std::move(component), std::move(filter) }; } /** * @brief Returns a group for the given components. * * This kind of objects are created on the fly and share with the registry * its internal data structures.
* Feel free to discard a group after the use. Creating and destroying a * group is an incredibly cheap operation because they do not require any * type of initialization, but for the first time they are requested.
* As a rule of thumb, storing a group should never be an option. * * Groups support exclusion lists and can own types of components. The more * types are owned by a group, the faster it is to iterate entities and * components.
* However, groups also affect some features of the registry such as the * creation and destruction of components, which will consequently be * slightly slower (nothing that can be noticed in most cases). * * @note * Pools of components that are owned by a group cannot be sorted anymore. * The group takes the ownership of the pools and arrange components so as * to iterate them as fast as possible. * * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); using handler_type = group_handler, get_t...>, std::remove_const_t...>; const auto cpools = std::make_tuple(assure>()..., assure>()...); constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); handler_type* handler = nullptr; if (auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return gdata.size == size && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash::value()) && ...); }); it != groups.cend()) { handler = static_cast(it->group.get()); } if (!handler) { group_data candidate = { size, { new handler_type{}, [](void* instance) { delete static_cast(instance); } }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, }; handler = static_cast(candidate.group.get()); const void* maybe_valid_if = nullptr; const void* discard_if = nullptr; if constexpr (sizeof...(Owned) == 0) { groups.push_back(std::move(candidate)); } else { ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [size](const auto& gdata) { const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash::value())); return !overlapping || ((sz == size) || (sz == gdata.size)); }), "Conflicting groups"); const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return !(0u + ... + gdata.owned(type_hash>::value())) || (size > gdata.size); }); const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto& gdata) { return (0u + ... + gdata.owned(type_hash>::value())); }); maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); groups.insert(next, std::move(candidate)); } (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); if constexpr (sizeof...(Owned) == 0) { for (const auto entity : view(exclude)) { handler->current.emplace(entity); } } else { // we cannot iterate backwards because we want to leave behind valid entities in case of owned types for (auto* first = std::get<0>(cpools)->data(), *last = first + std::get<0>(cpools)->size(); first != last; ++first) { handler->template maybe_valid_if...>>>(*this, *first); } } } return { handler->current, *std::get>*>(cpools)..., *std::get>*>(cpools)... }; } /** * @brief Returns a group for the given components. * * @sa group * * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t...>, std::add_const_t...> group_if_exists(get_t, exclude_t = {}) const { if (auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto& gdata) { return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash::value()) && ...); }); it == groups.cend()) { return {}; } else { using handler_type = group_handler, get_t...>, std::remove_const_t...>; return { static_cast(it->group.get())->current, *pool_if_exists>()... , *pool_if_exists>()... }; } } /** * @brief Returns a group for the given components. * * @sa group * * @tparam Owned Types of components owned by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t<>, Owned...> group(exclude_t = {}) { return group(get_t<>{}, exclude); } /** * @brief Returns a group for the given components. * * @sa group_if_exists * * @tparam Owned Types of components owned by the group. * @tparam Exclude Types of components used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group, get_t<>, std::add_const_t...> group_if_exists(exclude_t = {}) const { return group_if_exists...>(get_t<>{}, exclude); } /** * @brief Checks whether the given components belong to any group. * @tparam Component Types of components in which one is interested. * @return True if the pools of the given components are sortable, false * otherwise. */ template [[nodiscard]] bool sortable() const { return std::none_of(groups.cbegin(), groups.cend(), [](auto&& gdata) { return (gdata.owned(type_hash>::value()) || ...); }); } /** * @brief Checks whether a group can be sorted. * @tparam Owned Types of components owned by the group. * @tparam Get Types of components observed by the group. * @tparam Exclude Types of components used to filter the group. * @return True if the group can be sorted, false otherwise. */ template [[nodiscard]] bool sortable(const basic_group, get_t, Owned...>&) ENTT_NOEXCEPT { constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); return std::find_if(groups.cbegin(), groups.cend(), [size](const auto& gdata) { return (0u + ... + gdata.owned(type_hash>::value())) && (size < gdata.size); }) == groups.cend(); } /** * @brief Sorts the pool of entities for the given component. * * The order of the elements in a pool is highly affected by assignments * of components to entities and deletions. Components are arranged to * maximize the performance during iterations and users should not make any * assumption on the order.
* This function can be used to impose an order to the elements in the pool * of the given component. The order is kept valid until a component of the * given type is assigned or removed from an entity. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(const Entity, const Entity); * bool(const Component &, const Component &); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * The comparison funtion object received by the sort function object hasn't * necessarily the type of the one passed along with the other parameters to * this member function. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam Component Type of components to sort. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); assure()->sort(std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sorts two pools of components in the same way. * * The order of the elements in a pool is highly affected by assignments * of components to entities and deletions. Components are arranged to * maximize the performance during iterations and users should not make any * assumption on the order. * * It happens that different pools of components must be sorted the same way * because of runtime and/or performance constraints. This function can be * used to order a pool of components according to the order between the * entities in another pool of components. * * @b How @b it @b works * * Being `A` and `B` the two sets where `B` is the master (the one the order * of which rules) and `A` is the slave (the one to sort), after a call to * this function an iterator for `A` will return the entities according to * the following rules: * * * All the entities in `A` that are also in `B` are returned first * according to the order they have in `B`. * * All the entities in `A` that are not in `B` are returned in no * particular order after all the other entities. * * Any subsequent change to `B` won't affect the order in `A`. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam To Type of components to sort. * @tparam From Type of components to use to sort. */ template void sort() { ENTT_ASSERT(sortable(), "Cannot sort owned storage"); assure()->respect(*assure()); } /** * @brief Visits an entity and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the components owned by the entity. * * @sa type_info * * @warning * It's not specified whether a component attached to or removed from the * given entity during the visit is returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param entity A valid entity identifier. * @param func A valid function object. */ template void visit(entity_type entity, Func func) const { for (auto pos = pools.size(); pos; --pos) { if (const auto& pdata = pools[pos - 1]; pdata.pool && pdata.pool->contains(entity)) { func(pdata.poly->value_type()); } } } /** * @brief Visits a registry and returns the type info for its components. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the components managed by the registry. * * @sa type_info * * @warning * It's not specified whether a component for which a pool is created during * the visit is returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void visit(Func func) const { for (auto pos = pools.size(); pos; --pos) { if (const auto& pdata = pools[pos - 1]; pdata.pool) { func(pdata.poly->value_type()); } } } /** * @brief Binds an object to the context of the registry. * * If the value already exists it is overwritten, otherwise a new instance * of the given type is created and initialized with the arguments provided. * * @tparam Type Type of object to set. * @tparam Args Types of arguments to use to construct the object. * @param args Parameters to use to initialize the value. * @return A reference to the newly created object. */ template Type& set(Args &&... args) { unset(); vars.emplace_back(std::in_place_type, std::forward(args)...); return any_cast(vars.back()); } /** * @brief Unsets a context variable if it exists. * @tparam Type Type of object to set. */ template void unset() { vars.erase(std::remove_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }), vars.end()); } /** * @brief Binds an object to the context of the registry. * * In case the context doesn't contain the given object, the parameters * provided are used to construct it. * * @tparam Type Type of object to set. * @tparam Args Types of arguments to use to construct the object. * @param args Parameters to use to initialize the object. * @return A reference to the object in the context of the registry. */ template [[nodiscard]] Type& ctx_or_set(Args &&... args) { auto* value = try_ctx(); return value ? *value : set(std::forward(args)...); } /** * @brief Returns a pointer to an object in the context of the registry. * @tparam Type Type of object to get. * @return A pointer to the object if it exists in the context of the * registry, a null pointer otherwise. */ template [[nodiscard]] std::add_const_t* try_ctx() const { auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto&& var) { return var.type() == type; }); return it == vars.cend() ? nullptr : any_cast>(&*it); } /*! @copydoc try_ctx */ template [[nodiscard]] Type* try_ctx() { auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }); return it == vars.end() ? nullptr : any_cast(&*it); } /** * @brief Returns a reference to an object in the context of the registry. * * @warning * Attempting to get a context variable that doesn't exist results in * undefined behavior. * * @tparam Type Type of object to get. * @return A valid reference to the object in the context of the registry. */ template [[nodiscard]] std::add_const_t& ctx() const { auto it = std::find_if(vars.cbegin(), vars.cend(), [type = type_id()](auto&& var) { return var.type() == type; }); ENTT_ASSERT(it != vars.cend(), "Invalid instance"); return any_cast&>(*it); } /*! @copydoc ctx */ template [[nodiscard]] Type& ctx() { auto it = std::find_if(vars.begin(), vars.end(), [type = type_id()](auto&& var) { return var.type() == type; }); ENTT_ASSERT(it != vars.end(), "Invalid instance"); return any_cast(*it); } /** * @brief Visits a registry and returns the type info for its context * variables. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const type_info); * @endcode * * Returned identifiers are those of the context variables currently set. * * @sa type_info * * @warning * It's not specified whether a context variable created during the visit is * returned or not to the caller. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void ctx(Func func) const { for (auto pos = vars.size(); pos; --pos) { func(vars[pos - 1].type()); } } private: std::vector> vars{}; mutable std::vector pools{}; std::vector groups{}; std::vector entities{}; entity_type free_list{ tombstone }; }; } #endif // #include "entity/runtime_view.hpp" #ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP #define ENTT_ENTITY_RUNTIME_VIEW_HPP #include #include #include #include #include // #include "../config/config.h" // #include "entity.hpp" // #include "sparse_set.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Runtime view. * * Runtime views iterate over those entities that have at least all the given * components in their bags. During initialization, a runtime view looks at the * number of entities available for each component and picks up a reference to * the smallest set of candidate entities in order to get a performance boost * when iterate.
* Order of elements during iterations are highly dependent on the order of the * underlying data structures. See sparse_set and its specializations for more * details. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by the views, unless * a pool was missing when the view was built (in this case, the view won't * have a valid reference and won't be updated accordingly). * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_runtime_view final { using basic_common_type = basic_sparse_set; using underlying_iterator = typename basic_common_type::iterator; class view_iterator final { [[nodiscard]] bool valid() const { const auto entt = *it; return (!stable_storage || (entt != tombstone)) && std::all_of(pools->begin()++, pools->end(), [entt](const auto* curr) { return curr->contains(entt); }) && std::none_of(filter->cbegin(), filter->cend(), [entt](const auto* curr) { return curr && curr->contains(entt); }); } public: using difference_type = typename underlying_iterator::difference_type; using value_type = typename underlying_iterator::value_type; using pointer = typename underlying_iterator::pointer; using reference = typename underlying_iterator::reference; using iterator_category = std::bidirectional_iterator_tag; view_iterator() ENTT_NOEXCEPT = default; view_iterator(const std::vector& cpools, const std::vector& ignore, underlying_iterator curr) ENTT_NOEXCEPT : pools{ &cpools }, filter{ &ignore }, it{ curr }, stable_storage{ std::any_of(pools->cbegin(), pools->cend(), [](const basic_common_type* cpool) { return (cpool->policy() == deletion_policy::in_place); }) } { if (it != (*pools)[0]->end() && !valid()) { ++(*this); } } view_iterator& operator++() { while (++it != (*pools)[0]->end() && !valid()); return *this; } view_iterator operator++(int) { view_iterator orig = *this; return ++(*this), orig; } view_iterator& operator--() ENTT_NOEXCEPT { while (--it != (*pools)[0]->begin() && !valid()); return *this; } view_iterator operator--(int) ENTT_NOEXCEPT { view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] bool operator==(const view_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const view_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] pointer operator->() const { return it.operator->(); } [[nodiscard]] reference operator*() const { return *operator->(); } private: const std::vector* pools; const std::vector* filter; underlying_iterator it; bool stable_storage; }; [[nodiscard]] bool valid() const { return !pools.empty() && pools.front(); } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ using iterator = view_iterator; /*! @brief Default constructor to use to create empty, invalid views. */ basic_runtime_view() ENTT_NOEXCEPT : pools{}, filter{} {} /** * @brief Constructs a runtime view from a set of storage classes. * @param cpools The storage for the types to iterate. * @param epools The storage for the types used to filter the view. */ basic_runtime_view(std::vector cpools, std::vector epools) ENTT_NOEXCEPT : pools{ std::move(cpools) }, filter{ std::move(epools) } { // brings the best candidate (if any) on front of the vector std::rotate(pools.begin(), std::min_element(pools.begin(), pools.end(), [](const auto* lhs, const auto* rhs) { return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); }), pools.end()); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const { return valid() ? pools.front()->size() : size_type{}; } /** * @brief Returns an iterator to the first entity that has the given * components. * * The returned iterator points to the first entity that has the given * components. If the view is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity that has the given components. */ [[nodiscard]] iterator begin() const { return valid() ? iterator{ pools, filter, pools[0]->begin() } : iterator{}; } /** * @brief Returns an iterator that is past the last entity that has the * given components. * * The returned iterator points to the entity following the last entity that * has the given components. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity that has the * given components. */ [[nodiscard]] iterator end() const { return valid() ? iterator{ pools, filter, pools[0]->end() } : iterator{}; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto* curr) { return curr->contains(entt); }) && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto* curr) { return curr && curr->contains(entt); }); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity. It is provided only with * the entity itself. To get the components, users can use the registry with * which the view was built.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for (const auto entity : *this) { func(entity); } } private: std::vector pools; std::vector filter; }; } #endif // #include "entity/snapshot.hpp" #ifndef ENTT_ENTITY_SNAPSHOT_HPP #define ENTT_ENTITY_SNAPSHOT_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "registry.hpp" namespace entt { /** * @brief Utility class to create snapshots from a registry. * * A _snapshot_ can be either a dump of the entire registry or a narrower * selection of components of interest.
* This type can be used in both cases if provided with a correctly configured * output archive. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_snapshot { using traits_type = entt_traits; template void get(Archive& archive, std::size_t sz, It first, It last) const { const auto view = reg->template view>(); archive(typename traits_type::entity_type(sz)); while (first != last) { const auto entt = *(first++); if (reg->template all_of(entt)) { std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt))); } } } template void component(Archive& archive, It first, It last, std::index_sequence) const { std::array size{}; auto begin = first; while (begin != last) { const auto entt = *(begin++); ((reg->template all_of(entt) ? ++size[Index] : size[Index]), ...); } (get(archive, size[Index], first, last), ...); } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_snapshot(const basic_registry& source) ENTT_NOEXCEPT : reg{ &source } {} /*! @brief Default move constructor. */ basic_snapshot(basic_snapshot&&) = default; /*! @brief Default move assignment operator. @return This snapshot. */ basic_snapshot& operator=(basic_snapshot&&) = default; /** * @brief Puts aside all the entities from the underlying registry. * * Entities are serialized along with their versions. Destroyed entities are * taken in consideration as well by this function. * * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot& entities(Archive& archive) const { const auto sz = reg->size(); archive(typename traits_type::entity_type(sz)); for (auto first = reg->data(), last = first + sz; first != last; ++first) { archive(*first); } archive(reg->released()); return *this; } /** * @brief Puts aside the given components. * * Each instance is serialized together with the entity to which it belongs. * Entities are serialized along with their versions. * * @tparam Component Types of components to serialize. * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot& component(Archive& archive) const { if constexpr (sizeof...(Component) == 1u) { const auto view = reg->template view(); (component(archive, view.data(), view.data() + view.size()), ...); return *this; } else { (component(archive), ...); return *this; } } /** * @brief Puts aside the given components for the entities in a range. * * Each instance is serialized together with the entity to which it belongs. * Entities are serialized along with their versions. * * @tparam Component Types of components to serialize. * @tparam Archive Type of output archive. * @tparam It Type of input iterator. * @param archive A valid reference to an output archive. * @param first An iterator to the first element of the range to serialize. * @param last An iterator past the last element of the range to serialize. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot& component(Archive& archive, It first, It last) const { component(archive, first, last, std::index_sequence_for{}); return *this; } private: const basic_registry* reg; }; /** * @brief Utility class to restore a snapshot as a whole. * * A snapshot loader requires that the destination registry be empty and loads * all the data at once while keeping intact the identifiers that the entities * originally had.
* An example of use is the implementation of a save/restore utility. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_snapshot_loader { using traits_type = entt_traits; template void assign(Archive& archive) const { typename traits_type::entity_type length{}; archive(length); entity_type entt{}; if constexpr (std::tuple_size_vtemplate view().get({})) > == 0) { while (length--) { archive(entt); const auto entity = reg->valid(entt) ? entt : reg->create(entt); ENTT_ASSERT(entity == entt, "Entity not available for use"); reg->template emplace(entity); } } else { Type instance{}; while (length--) { archive(entt, instance); const auto entity = reg->valid(entt) ? entt : reg->create(entt); ENTT_ASSERT(entity == entt, "Entity not available for use"); reg->template emplace(entity, std::move(instance)); } } } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_snapshot_loader(basic_registry& source) ENTT_NOEXCEPT : reg{ &source } { // restoring a snapshot as a whole requires a clean registry ENTT_ASSERT(reg->empty(), "Registry must be empty"); } /*! @brief Default move constructor. */ basic_snapshot_loader(basic_snapshot_loader&&) = default; /*! @brief Default move assignment operator. @return This loader. */ basic_snapshot_loader& operator=(basic_snapshot_loader&&) = default; /** * @brief Restores entities that were in use during serialization. * * This function restores the entities that were in use during serialization * and gives them the versions they originally had. * * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template const basic_snapshot_loader& entities(Archive& archive) const { typename traits_type::entity_type length{}; archive(length); std::vector all(length); for (decltype(length) pos{}; pos < length; ++pos) { archive(all[pos]); } entity_type destroyed; archive(destroyed); reg->assign(all.cbegin(), all.cend(), destroyed); return *this; } /** * @brief Restores components and assigns them to the right entities. * * The template parameter list must be exactly the same used during * serialization. In the event that the entity to which the component is * assigned doesn't exist yet, the loader will take care to create it with * the version it originally had. * * @tparam Component Types of components to restore. * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template const basic_snapshot_loader& component(Archive& archive) const { (assign(archive), ...); return *this; } /** * @brief Destroys those entities that have no components. * * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
* This functions helps to identify and destroy those entities. * * @return A valid loader to continue restoring data. */ const basic_snapshot_loader& orphans() const { reg->orphans([this](const auto entt) { reg->release(entt); }); return *this; } private: basic_registry* reg; }; /** * @brief Utility class for _continuous loading_. * * A _continuous loader_ is designed to load data from a source registry to a * (possibly) non-empty destination. The loader can accommodate in a registry * more than one snapshot in a sort of _continuous loading_ that updates the * destination one step at a time.
* Identifiers that entities originally had are not transferred to the target. * Instead, the loader maps remote identifiers to local ones while restoring a * snapshot.
* An example of use is the implementation of a client-server applications with * the requirement of transferring somehow parts of the representation side to * side. * * @tparam Entity A valid entity type (see entt_traits for more details). */ template class basic_continuous_loader { using traits_type = entt_traits; void destroy(Entity entt) { if (const auto it = remloc.find(entt); it == remloc.cend()) { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); reg->destroy(local); } } void restore(Entity entt) { const auto it = remloc.find(entt); if (it == remloc.cend()) { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); } else { if (!reg->valid(remloc[entt].first)) { remloc[entt].first = reg->create(); } // set the dirty flag remloc[entt].second = true; } } template auto update(int, Container& container) -> decltype(typename Container::mapped_type{}, void()) { // map like container Container other; for (auto&& pair : container) { using first_type = std::remove_const_t::first_type>; using second_type = typename std::decay_t::second_type; if constexpr (std::is_same_v && std::is_same_v) { other.emplace(map(pair.first), map(pair.second)); } else if constexpr (std::is_same_v) { other.emplace(map(pair.first), std::move(pair.second)); } else { static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); other.emplace(std::move(pair.first), map(pair.second)); } } std::swap(container, other); } template auto update(char, Container& container) -> decltype(typename Container::value_type{}, void()) { // vector like container static_assert(std::is_same_v, "Invalid value type"); for (auto&& entt : container) { entt = map(entt); } } template void update([[maybe_unused]] Other& instance, [[maybe_unused]] Member Type::* member) { if constexpr (!std::is_same_v) { return; } else if constexpr (std::is_same_v) { instance.*member = map(instance.*member); } else { // maybe a container? let's try... update(0, instance.*member); } } template void remove_if_exists() { for (auto&& ref : remloc) { const auto local = ref.second.first; if (reg->valid(local)) { reg->template remove(local); } } } template void assign(Archive& archive, [[maybe_unused]] Member Type:: *... member) { typename traits_type::entity_type length{}; archive(length); entity_type entt{}; if constexpr (std::tuple_size_vtemplate view().get({})) > == 0) { while (length--) { archive(entt); restore(entt); reg->template emplace_or_replace(map(entt)); } } else { Other instance{}; while (length--) { archive(entt, instance); (update(instance, member), ...); restore(entt); reg->template emplace_or_replace(map(entt), std::move(instance)); } } } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_continuous_loader(basic_registry& source) ENTT_NOEXCEPT : reg{ &source } {} /*! @brief Default move constructor. */ basic_continuous_loader(basic_continuous_loader&&) = default; /*! @brief Default move assignment operator. @return This loader. */ basic_continuous_loader& operator=(basic_continuous_loader&&) = default; /** * @brief Restores entities that were in use during serialization. * * This function restores the entities that were in use during serialization * and creates local counterparts for them if required. * * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A non-const reference to this loader. */ template basic_continuous_loader& entities(Archive& archive) { typename traits_type::entity_type length{}; entity_type entt{}; archive(length); for (decltype(length) pos{}; pos < length; ++pos) { archive(entt); if (const auto entity = traits_type::to_entity(entt); entity == pos) { restore(entt); } else { destroy(entt); } } // discards the head of the list of destroyed entities archive(entt); return *this; } /** * @brief Restores components and assigns them to the right entities. * * The template parameter list must be exactly the same used during * serialization. In the event that the entity to which the component is * assigned doesn't exist yet, the loader will take care to create a local * counterpart for it.
* Members can be either data members of type entity_type or containers of * entities. In both cases, the loader will visit them and update the * entities by replacing each one with its local counterpart. * * @tparam Component Type of component to restore. * @tparam Archive Type of input archive. * @tparam Type Types of components to update with local counterparts. * @tparam Member Types of members to update with their local counterparts. * @param archive A valid reference to an input archive. * @param member Members to update with their local counterparts. * @return A non-const reference to this loader. */ template basic_continuous_loader& component(Archive& archive, Member Type:: *... member) { (remove_if_exists(), ...); (assign(archive, member...), ...); return *this; } /** * @brief Helps to purge entities that no longer have a conterpart. * * Users should invoke this member function after restoring each snapshot, * unless they know exactly what they are doing. * * @return A non-const reference to this loader. */ basic_continuous_loader& shrink() { auto it = remloc.begin(); while (it != remloc.cend()) { const auto local = it->second.first; bool& dirty = it->second.second; if (dirty) { dirty = false; ++it; } else { if (reg->valid(local)) { reg->destroy(local); } it = remloc.erase(it); } } return *this; } /** * @brief Destroys those entities that have no components. * * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
* This functions helps to identify and destroy those entities. * * @return A non-const reference to this loader. */ basic_continuous_loader& orphans() { reg->orphans([this](const auto entt) { reg->release(entt); }); return *this; } /** * @brief Tests if a loader knows about a given entity. * @param entt An entity identifier. * @return True if `entity` is managed by the loader, false otherwise. */ [[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT { return (remloc.find(entt) != remloc.cend()); } /** * @brief Returns the identifier to which an entity refers. * @param entt An entity identifier. * @return The local identifier if any, the null entity otherwise. */ [[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT { const auto it = remloc.find(entt); entity_type other = null; if (it != remloc.cend()) { other = it->second.first; } return other; } private: std::unordered_map> remloc; basic_registry* reg; }; } #endif // #include "entity/sparse_set.hpp" #ifndef ENTT_ENTITY_SPARSE_SET_HPP #define ENTT_ENTITY_SPARSE_SET_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/fwd.hpp" // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /*! @brief Sparse set deletion policy. */ enum class deletion_policy : std::uint8_t { /*! @brief Swap-and-pop deletion policy. */ swap_and_pop = 0u, /*! @brief In-place deletion policy. */ in_place = 1u }; /** * @brief Basic sparse set implementation. * * Sparse set or packed array or whatever is the name users give it.
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
* This is largely used by the registry to offer users the fastest access ever * to the components. Views and groups in general are almost entirely designed * around sparse sets. * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_sparse_set { static constexpr auto growth_factor = 1.5; static constexpr auto sparse_page = ENTT_SPARSE_PAGE; using traits_type = entt_traits; using alloc_traits = typename std::allocator_traits::template rebind_traits; using alloc_pointer = typename alloc_traits::pointer; using alloc_const_pointer = typename alloc_traits::const_pointer; using bucket_alloc_traits = typename std::allocator_traits::template rebind_traits; using bucket_alloc_pointer = typename bucket_alloc_traits::pointer; static_assert(alloc_traits::propagate_on_container_move_assignment::value); static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value); struct sparse_set_iterator final { using difference_type = typename traits_type::difference_type; using value_type = Entity; using pointer = const value_type*; using reference = const value_type&; using iterator_category = std::random_access_iterator_tag; sparse_set_iterator() ENTT_NOEXCEPT = default; sparse_set_iterator(const alloc_const_pointer* ref, const difference_type idx) ENTT_NOEXCEPT : packed{ ref }, index{ idx } {} sparse_set_iterator& operator++() ENTT_NOEXCEPT { return --index, * this; } sparse_set_iterator operator++(int) ENTT_NOEXCEPT { iterator orig = *this; return ++(*this), orig; } sparse_set_iterator& operator--() ENTT_NOEXCEPT { return ++index, * this; } sparse_set_iterator operator--(int) ENTT_NOEXCEPT { sparse_set_iterator orig = *this; return operator--(), orig; } sparse_set_iterator& operator+=(const difference_type value) ENTT_NOEXCEPT { index -= value; return *this; } sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { sparse_set_iterator copy = *this; return (copy += value); } sparse_set_iterator& operator-=(const difference_type value) ENTT_NOEXCEPT { return (*this += -value); } sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { return (*this + -value); } difference_type operator-(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return other.index - index; } [[nodiscard]] reference operator[](const difference_type value) const { const auto pos = size_type(index - value - 1u); return (*packed)[pos]; } [[nodiscard]] bool operator==(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return other.index == index; } [[nodiscard]] bool operator!=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] bool operator<(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return index > other.index; } [[nodiscard]] bool operator>(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return index < other.index; } [[nodiscard]] bool operator<=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this > other); } [[nodiscard]] bool operator>=(const sparse_set_iterator& other) const ENTT_NOEXCEPT { return !(*this < other); } [[nodiscard]] pointer operator->() const { const auto pos = size_type(index - 1u); return std::addressof((*packed)[pos]); } [[nodiscard]] reference operator*() const { return *operator->(); } private: const alloc_const_pointer* packed; difference_type index; }; [[nodiscard]] static auto page(const Entity entt) ENTT_NOEXCEPT { return size_type{ traits_type::to_entity(entt) / sparse_page }; } [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT { return size_type{ traits_type::to_entity(entt) & (sparse_page - 1) }; } [[nodiscard]] auto assure_page(const std::size_t idx) { if (!(idx < bucket)) { const size_type sz = idx + 1u; const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz); std::uninitialized_value_construct(mem + bucket, mem + sz); std::uninitialized_copy(sparse, sparse + bucket, mem); std::destroy(sparse, sparse + bucket); bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket); sparse = mem; bucket = sz; } if (!sparse[idx]) { sparse[idx] = alloc_traits::allocate(allocator, sparse_page); std::uninitialized_fill(sparse[idx], sparse[idx] + sparse_page, null); } return sparse[idx]; } void resize_packed(const std::size_t req) { ENTT_ASSERT((req != reserved) && !(req < count), "Invalid request"); const auto mem = alloc_traits::allocate(allocator, req); std::uninitialized_copy(packed, packed + count, mem); std::uninitialized_fill(mem + count, mem + req, tombstone); std::destroy(packed, packed + reserved); alloc_traits::deallocate(allocator, packed, reserved); packed = mem; reserved = req; } void release_memory() { if (packed) { for (size_type pos{}; pos < bucket; ++pos) { if (sparse[pos]) { std::destroy(sparse[pos], sparse[pos] + sparse_page); alloc_traits::deallocate(allocator, sparse[pos], sparse_page); } } std::destroy(packed, packed + reserved); std::destroy(sparse, sparse + bucket); alloc_traits::deallocate(allocator, packed, reserved); bucket_alloc_traits::deallocate(bucket_allocator, sparse, bucket); } } protected: /** * @brief Swaps two entities in the internal packed array. * @param lhs A valid position of an entity within storage. * @param rhs A valid position of an entity within storage. */ virtual void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) {} /** * @brief Moves an entity in the internal packed array. * @param from A valid position of an entity within storage. * @param to A valid position of an entity within storage. */ virtual void move_and_pop([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) {} /** * @brief Attempts to erase an entity from the internal packed array. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ virtual void swap_and_pop(const Entity entt, [[maybe_unused]] void* ud) { auto& ref = sparse[page(entt)][offset(entt)]; const auto pos = size_type{ traits_type::to_entity(ref) }; ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier"); auto& last = packed[--count]; packed[pos] = last; sparse[page(last)][offset(last)] = ref; // lazy self-assignment guard ref = null; // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((last = tombstone, true), ""); } /** * @brief Attempts to erase an entity from the internal packed array. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ virtual void in_place_pop(const Entity entt, [[maybe_unused]] void* ud) { auto& ref = sparse[page(entt)][offset(entt)]; const auto pos = size_type{ traits_type::to_entity(ref) }; ENTT_ASSERT(packed[pos] == entt, "Invalid entity identifier"); packed[pos] = std::exchange(free_list, traits_type::construct(static_cast(pos))); // lazy self-assignment guard ref = null; } public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = alloc_const_pointer; /*! @brief Random access iterator type. */ using iterator = sparse_set_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /** * @brief Constructs an empty container with the given policy and allocator. * @param pol Type of deletion policy. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_sparse_set(deletion_policy pol, const allocator_type& alloc = {}) : allocator{ alloc }, bucket_allocator{ alloc }, sparse{ bucket_alloc_traits::allocate(bucket_allocator, 0u) }, packed{ alloc_traits::allocate(allocator, 0u) }, bucket{ 0u }, count{ 0u }, reserved{ 0u }, free_list{ tombstone }, mode{ pol } {} /** * @brief Constructs an empty container with the given allocator. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_sparse_set(const allocator_type& alloc = {}) : basic_sparse_set{ deletion_policy::swap_and_pop, alloc } {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_sparse_set(basic_sparse_set&& other) ENTT_NOEXCEPT : allocator{ std::move(other.allocator) }, bucket_allocator{ std::move(other.bucket_allocator) }, sparse{ std::exchange(other.sparse, bucket_alloc_pointer{}) }, packed{ std::exchange(other.packed, alloc_pointer{}) }, bucket{ std::exchange(other.bucket, 0u) }, count{ std::exchange(other.count, 0u) }, reserved{ std::exchange(other.reserved, 0u) }, free_list{ std::exchange(other.free_list, tombstone) }, mode{ other.mode } {} /*! @brief Default destructor. */ virtual ~basic_sparse_set() { release_memory(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_sparse_set& operator=(basic_sparse_set&& other) ENTT_NOEXCEPT { release_memory(); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); sparse = std::exchange(other.sparse, bucket_alloc_pointer{}); packed = std::exchange(other.packed, alloc_pointer{}); bucket = std::exchange(other.bucket, 0u); count = std::exchange(other.count, 0u); reserved = std::exchange(other.reserved, 0u); free_list = std::exchange(other.free_list, tombstone); mode = other.mode; return *this; } /** * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ [[nodiscard]] deletion_policy policy() const ENTT_NOEXCEPT { return mode; } /** * @brief Returns the next slot available for insertion. * @return The next slot available for insertion. */ [[nodiscard]] size_type slot() const ENTT_NOEXCEPT { return free_list == null ? count : size_type{ traits_type::to_entity(free_list) }; } /** * @brief Increases the capacity of a sparse set. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) { if (cap > reserved) { resize_packed(cap); } } /** * @brief Returns the number of elements that a sparse set has currently * allocated space for. * @return Capacity of the sparse set. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return reserved; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if (count < reserved) { resize_packed(count); } } /** * @brief Returns the extent of a sparse set. * * The extent of a sparse set is also the size of the internal sparse array. * There is no guarantee that the internal packed array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const ENTT_NOEXCEPT { return bucket * sparse_page; } /** * @brief Returns the number of elements in a sparse set. * * The number of elements is also the size of the internal packed array. * There is no guarantee that the internal sparse array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Number of elements. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return count; } /** * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return (count == size_type{}); } /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ [[nodiscard]] pointer data() const ENTT_NOEXCEPT { return packed; } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first entity of the internal packed * array. If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the internal packed array. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ std::addressof(packed), static_cast(count) }; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last entity in * the internal packed array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last entity of the * internal packed array. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{ std::addressof(packed), {} }; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first entity of the reversed internal * packed array. If the sparse set is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last entity in * the reversed internal packed array. Attempting to dereference the * returned iterator results in undefined behavior. * * @return An iterator to the element following the last entity of the * reversed internal packed array. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return std::make_reverse_iterator(begin()); } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const ENTT_NOEXCEPT { return contains(entt) ? --(end() - index(entt)) : end(); } /** * @brief Checks if a sparse set contains an entity. * @param entt A valid entity identifier. * @return True if the sparse set contains the entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(entt != tombstone && entt != null, "Invalid entity"); const auto curr = page(entt); // testing versions permits to avoid accessing the packed array return (curr < bucket&& sparse[curr] && sparse[curr][offset(entt)] != null); } /** * @brief Returns the position of an entity in a sparse set. * * @warning * Attempting to get the position of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid entity identifier. * @return The position of the entity in the sparse set. */ [[nodiscard]] size_type index(const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt), "Set does not contain entity"); return size_type{ traits_type::to_entity(sparse[page(entt)][offset(entt)]) }; } /** * @brief Returns the entity at specified location, with bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ [[nodiscard]] entity_type at(const size_type pos) const ENTT_NOEXCEPT { return pos < count ? packed[pos] : null; } /** * @brief Returns the entity at specified location, without bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location. */ [[nodiscard]] entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { ENTT_ASSERT(pos < count, "Position is out of bounds"); return packed[pos]; } /** * @brief Appends an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @return The slot used for insertion. */ size_type emplace_back(const entity_type entt) { ENTT_ASSERT(!contains(entt), "Set already contains entity"); if (count == reserved) { const size_type sz = static_cast(reserved * growth_factor); resize_packed(sz + !(sz > reserved)); } assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast(count)); packed[count] = entt; return count++; } /** * @brief Assigns an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @return The slot used for insertion. */ size_type emplace(const entity_type entt) { if (free_list == null) { return emplace_back(entt); } else { ENTT_ASSERT(!contains(entt), "Set already contains entity"); const auto pos = size_type{ traits_type::to_entity(free_list) }; sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast(pos)); free_list = std::exchange(packed[pos], entt); return pos; } } /** * @brief Assigns one or more entities to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last) { reserve(count + std::distance(first, last)); for (; first != last; ++first) { const auto entt = *first; ENTT_ASSERT(!contains(entt), "Set already contains entity"); assure_page(page(entt))[offset(entt)] = traits_type::construct(static_cast(count)); packed[count++] = entt; } } /** * @brief Erases an entity from a sparse set. * * @warning * Attempting to erase an entity that doesn't belong to the sparse set * results in undefined behavior. * * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. */ void erase(const entity_type entt, void* ud = nullptr) { ENTT_ASSERT(contains(entt), "Set does not contain entity"); (mode == deletion_policy::in_place) ? in_place_pop(entt, ud) : swap_and_pop(entt, ud); } /** * @brief Erases entities from a set. * * @sa erase * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param ud Optional user data that are forwarded as-is to derived classes. */ template void erase(It first, It last, void* ud = nullptr) { for (; first != last; ++first) { erase(*first, ud); } } /** * @brief Removes an entity from a sparse set if it exists. * @param entt A valid entity identifier. * @param ud Optional user data that are forwarded as-is to derived classes. * @return True if the entity is actually removed, false otherwise. */ bool remove(const entity_type entt, void* ud = nullptr) { return contains(entt) && (erase(entt, ud), true); } /** * @brief Removes entities from a sparse set if they exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param ud Optional user data that are forwarded as-is to derived classes. * @return The number of entities actually removed. */ template size_type remove(It first, It last, void* ud = nullptr) { size_type found{}; for (; first != last; ++first) { found += remove(*first, ud); } return found; } /*! @brief Removes all tombstones from the packed array of a sparse set. */ void compact() { size_type next = count; for (; next && packed[next - 1u] == tombstone; --next); for (auto* it = &free_list; *it != null && next; it = std::addressof(packed[traits_type::to_entity(*it)])) { if (const size_type pos = traits_type::to_entity(*it); pos < next) { --next; move_and_pop(next, pos); std::swap(packed[next], packed[pos]); sparse[page(packed[pos])][offset(packed[pos])] = traits_type::construct(static_cast(pos)); *it = traits_type::construct(static_cast(next)); for (; next && packed[next - 1u] == tombstone; --next); } } free_list = tombstone; count = next; } /** * @copybrief swap_at * * For what it's worth, this function affects both the internal sparse array * and the internal packed array. Users should not care of that anyway. * * @warning * Attempting to swap entities that don't belong to the sparse set results * in undefined behavior. * * @param lhs A valid entity identifier. * @param rhs A valid entity identifier. */ void swap(const entity_type lhs, const entity_type rhs) { ENTT_ASSERT(contains(lhs), "Set does not contain entity"); ENTT_ASSERT(contains(rhs), "Set does not contain entity"); auto& entt = sparse[page(lhs)][offset(lhs)]; auto& other = sparse[page(rhs)][offset(rhs)]; const auto from = size_type{ traits_type::to_entity(entt) }; const auto to = size_type{ traits_type::to_entity(other) }; // basic no-leak guarantee (with invalid state) if swapping throws swap_at(from, to); std::swap(entt, other); std::swap(packed[from], packed[to]); } /** * @brief Sort the first count elements according to the given comparison * function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to the following: * * @code{.cpp} * bool(const Entity, const Entity); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) { // basic no-leak guarantee (with invalid state) if sorting throws ENTT_ASSERT(!(length > count), "Length exceeds the number of elements"); compact(); algo(std::make_reverse_iterator(packed + length), std::make_reverse_iterator(packed), std::move(compare), std::forward(args)...); for (size_type pos{}; pos < length; ++pos) { auto curr = pos; auto next = index(packed[curr]); while (curr != next) { const auto idx = index(packed[next]); const auto entt = packed[curr]; swap_at(next, idx); sparse[page(entt)][offset(entt)] = traits_type::construct(static_cast(curr)); curr = std::exchange(next, idx); } } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { sort_n(count, std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally * according to the order they have in `other`. All the other entities goes * to the end of the list and there are no guarantees on their order.
* In other terms, this function can be used to impose the same order on two * sets by using one of them as a master and the other one as a slave. * * Iterating the sparse set with a couple of iterators returns elements in * the expected order after a call to `respect`. See `begin` and `end` for * more details. * * @param other The sparse sets that imposes the order of the entities. */ void respect(const basic_sparse_set& other) { compact(); const auto to = other.end(); auto from = other.begin(); for (size_type pos = count - 1; pos && from != to; ++from) { if (contains(*from)) { if (*from != packed[pos]) { // basic no-leak guarantee (with invalid state) if swapping throws swap(packed[pos], *from); } --pos; } } } /** * @brief Clears a sparse set. * @param ud Optional user data that are forwarded as-is to derived classes. */ void clear(void* ud = nullptr) { for (auto&& entity : *this) { if (entity != tombstone) { in_place_pop(entity, ud); } } free_list = tombstone; count = 0u; } private: typename alloc_traits::allocator_type allocator; typename bucket_alloc_traits::allocator_type bucket_allocator; bucket_alloc_pointer sparse; alloc_pointer packed; std::size_t bucket; std::size_t count; std::size_t reserved; entity_type free_list; deletion_policy mode; }; } #endif // #include "entity/storage.hpp" #ifndef ENTT_ENTITY_STORAGE_HPP #define ENTT_ENTITY_STORAGE_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/sigh.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" namespace entt { /** * @brief Basic storage implementation. * * This class is a refinement of a sparse set that associates an object to an * entity. The main purpose of this class is to extend sparse sets to store * components in a registry. It guarantees fast access both to the elements and * to the entities. * * @note * Entities and objects have the same order. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that objects are returned in the insertion order when iterate * a storage. Do not make assumption on the order in any case. * * @warning * Empty types aren't explicitly instantiated. Therefore, many of the functions * normally available for non-empty types will not be available for empty ones. * * @sa sparse_set * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_storage_impl : public basic_sparse_set::template rebind_alloc> { static constexpr auto packed_page = ENTT_PACKED_PAGE; using comp_traits = component_traits; using underlying_type = basic_sparse_set::template rebind_alloc>; using difference_type = typename entt_traits::difference_type; using alloc_traits = typename std::allocator_traits::template rebind_traits; using alloc_pointer = typename alloc_traits::pointer; using alloc_const_pointer = typename alloc_traits::const_pointer; using bucket_alloc_traits = typename std::allocator_traits::template rebind_traits; using bucket_alloc_pointer = typename bucket_alloc_traits::pointer; using bucket_alloc_const_type = typename std::allocator_traits::template rebind_alloc; using bucket_alloc_const_pointer = typename std::allocator_traits::const_pointer; static_assert(alloc_traits::propagate_on_container_move_assignment::value); static_assert(bucket_alloc_traits::propagate_on_container_move_assignment::value); template struct storage_iterator final { using difference_type = typename basic_storage_impl::difference_type; using value_type = Value; using pointer = value_type*; using reference = value_type&; using iterator_category = std::random_access_iterator_tag; storage_iterator() ENTT_NOEXCEPT = default; storage_iterator(bucket_alloc_pointer const* ref, const typename basic_storage_impl::difference_type idx) ENTT_NOEXCEPT : packed{ ref }, index{ idx } {} storage_iterator& operator++() ENTT_NOEXCEPT { return --index, * this; } storage_iterator operator++(int) ENTT_NOEXCEPT { storage_iterator orig = *this; return ++(*this), orig; } storage_iterator& operator--() ENTT_NOEXCEPT { return ++index, * this; } storage_iterator operator--(int) ENTT_NOEXCEPT { storage_iterator orig = *this; return operator--(), orig; } storage_iterator& operator+=(const difference_type value) ENTT_NOEXCEPT { index -= value; return *this; } storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { storage_iterator copy = *this; return (copy += value); } storage_iterator& operator-=(const difference_type value) ENTT_NOEXCEPT { return (*this += -value); } storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { return (*this + -value); } difference_type operator-(const storage_iterator& other) const ENTT_NOEXCEPT { return other.index - index; } [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { const auto pos = size_type(index - value - 1); return (*packed)[page(pos)][offset(pos)]; } [[nodiscard]] bool operator==(const storage_iterator& other) const ENTT_NOEXCEPT { return other.index == index; } [[nodiscard]] bool operator!=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] bool operator<(const storage_iterator& other) const ENTT_NOEXCEPT { return index > other.index; } [[nodiscard]] bool operator>(const storage_iterator& other) const ENTT_NOEXCEPT { return index < other.index; } [[nodiscard]] bool operator<=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this > other); } [[nodiscard]] bool operator>=(const storage_iterator& other) const ENTT_NOEXCEPT { return !(*this < other); } [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { const auto pos = size_type(index - 1u); return std::addressof((*packed)[page(pos)][offset(pos)]); } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return *operator->(); } private: bucket_alloc_pointer const* packed; difference_type index; }; [[nodiscard]] static auto page(const std::size_t pos) ENTT_NOEXCEPT { return pos / packed_page; } [[nodiscard]] static auto offset(const std::size_t pos) ENTT_NOEXCEPT { return pos & (packed_page - 1); } void release_memory() { if (packed) { // no-throw stable erase iteration underlying_type::clear(); for (size_type pos{}; pos < bucket; ++pos) { alloc_traits::deallocate(allocator, packed[pos], packed_page); bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos])); } bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); } } void assure_at_least(const std::size_t last) { if (const auto idx = page(last - 1u); !(idx < bucket)) { const size_type sz = idx + 1u; const auto mem = bucket_alloc_traits::allocate(bucket_allocator, sz); std::uninitialized_copy(packed, packed + bucket, mem); size_type pos{}; ENTT_TRY{ for (pos = bucket; pos < sz; ++pos) { auto pg = alloc_traits::allocate(allocator, packed_page); bucket_alloc_traits::construct(bucket_allocator, std::addressof(mem[pos]), pg); } } ENTT_CATCH{ for (auto next = bucket; next < pos; ++next) { alloc_traits::deallocate(allocator, mem[next], packed_page); } std::destroy(mem, mem + pos); bucket_alloc_traits::deallocate(bucket_allocator, mem, sz); ENTT_THROW; } std::destroy(packed, packed + bucket); bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); packed = mem; bucket = sz; } } void release_unused_pages() { if (const auto length = underlying_type::size() / packed_page; length < bucket) { const auto mem = bucket_alloc_traits::allocate(bucket_allocator, length); std::uninitialized_copy(packed, packed + length, mem); for (auto pos = length; pos < bucket; ++pos) { alloc_traits::deallocate(allocator, packed[pos], packed_page); bucket_alloc_traits::destroy(bucket_allocator, std::addressof(packed[pos])); } bucket_alloc_traits::deallocate(bucket_allocator, packed, bucket); packed = mem; bucket = length; } } template auto& push_at(const std::size_t pos, Args &&... args) { ENTT_ASSERT(pos < (bucket* packed_page), "Out of bounds index"); auto* instance = std::addressof(packed[page(pos)][offset(pos)]); if constexpr (std::is_aggregate_v) { alloc_traits::construct(allocator, instance, Type{ std::forward(args)... }); } else { alloc_traits::construct(allocator, instance, std::forward(args)...); } return *instance; } void pop_at(const std::size_t pos) { alloc_traits::destroy(allocator, std::addressof(packed[page(pos)][offset(pos)])); } protected: /*! @copydoc basic_sparse_set::swap_at */ void swap_at(const std::size_t lhs, const std::size_t rhs) final { std::swap(packed[page(lhs)][offset(lhs)], packed[page(rhs)][offset(rhs)]); } /*! @copydoc basic_sparse_set::move_and_pop */ void move_and_pop(const std::size_t from, const std::size_t to) final { push_at(to, std::move(packed[page(from)][offset(from)])); pop_at(from); } /*! @copydoc basic_sparse_set::swap_and_pop */ void swap_and_pop(const Entity entt, void* ud) override { const auto pos = underlying_type::index(entt); const auto last = underlying_type::size() - 1u; auto&& elem = packed[page(pos)][offset(pos)]; // support for nosy destructors [[maybe_unused]] auto unused = std::move(elem); elem = std::move(packed[page(last)][offset(last)]); pop_at(last); underlying_type::swap_and_pop(entt, ud); } /*! @copydoc basic_sparse_set::in_place_pop */ void in_place_pop(const Entity entt, void* ud) override { const auto pos = underlying_type::index(entt); underlying_type::in_place_pop(entt, ud); // support for nosy destructors pop_at(pos); } public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained elements. */ using pointer = bucket_alloc_pointer; /*! @brief Constant pointer type to contained elements. */ using const_pointer = bucket_alloc_const_pointer; /*! @brief Random access iterator type. */ using iterator = storage_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = std::reverse_iterator; /** * @brief Default constructor. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_storage_impl(const allocator_type& alloc = {}) : underlying_type{ deletion_policy{comp_traits::in_place_delete::value}, alloc }, allocator{ alloc }, bucket_allocator{ alloc }, packed{ bucket_alloc_traits::allocate(bucket_allocator, 0u) }, bucket{} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage_impl(basic_storage_impl&& other) ENTT_NOEXCEPT : underlying_type{ std::move(other) }, allocator{ std::move(other.allocator) }, bucket_allocator{ std::move(other.bucket_allocator) }, packed{ std::exchange(other.packed, bucket_alloc_pointer{}) }, bucket{ std::exchange(other.bucket, 0u) } {} /*! @brief Default destructor. */ ~basic_storage_impl() override { release_memory(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_storage_impl& operator=(basic_storage_impl&& other) ENTT_NOEXCEPT { release_memory(); underlying_type::operator=(std::move(other)); allocator = std::move(other.allocator); bucket_allocator = std::move(other.bucket_allocator); packed = std::exchange(other.packed, bucket_alloc_pointer{}); bucket = std::exchange(other.bucket, 0u); return *this; } /** * @brief Increases the capacity of a storage. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) { underlying_type::reserve(cap); if (cap > underlying_type::size()) { assure_at_least(cap); } } /** * @brief Returns the number of elements that a storage has currently * allocated space for. * @return Capacity of the storage. */ [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { return bucket * packed_page; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { underlying_type::shrink_to_fit(); release_unused_pages(); } /** * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { return packed; } /*! @copydoc raw */ [[nodiscard]] pointer raw() ENTT_NOEXCEPT { return packed; } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the storage is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { const difference_type pos = underlying_type::size(); return const_iterator{ std::addressof(packed), pos }; } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() ENTT_NOEXCEPT { const difference_type pos = underlying_type::size(); return iterator{ std::addressof(packed), pos }; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { return const_iterator{ std::addressof(packed), {} }; } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() ENTT_NOEXCEPT { return iterator{ std::addressof(packed), {} }; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first instance of the reversed * internal array. If the storage is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first instance of the reversed internal array. */ [[nodiscard]] const_reverse_iterator crbegin() const ENTT_NOEXCEPT { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { return crbegin(); } /*! @copydoc rbegin */ [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last instance * of the reversed internal array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last instance of the * reversed internal array. */ [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { return crend(); } /*! @copydoc rend */ [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { return std::make_reverse_iterator(begin()); } /** * @brief Returns the object assigned to an entity. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid entity identifier. * @return The object assigned to the entity. */ [[nodiscard]] const value_type& get(const entity_type entt) const ENTT_NOEXCEPT { const auto idx = underlying_type::index(entt); return packed[page(idx)][offset(idx)]; } /*! @copydoc get */ [[nodiscard]] value_type& get(const entity_type entt) ENTT_NOEXCEPT { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Assigns an entity to a storage and constructs its object. * * This version accept both types that can be constructed in place directly * and types like aggregates that do not work well with a placement new as * performed usually under the hood during an _emplace back_. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to construct an object for the entity. * @return A reference to the newly created object. */ template value_type& emplace(const entity_type entt, Args &&... args) { const auto pos = underlying_type::slot(); assure_at_least(pos + 1u); auto& value = push_at(pos, std::forward(args)...); ENTT_TRY{ [[maybe_unused]] const auto curr = underlying_type::emplace(entt); ENTT_ASSERT(pos == curr, "Misplaced component"); } ENTT_CATCH{ pop_at(pos); ENTT_THROW; } return value; } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the updated instance. */ template decltype(auto) patch(const entity_type entt, Func &&... func) { const auto idx = underlying_type::index(entt); auto&& elem = packed[page(idx)][offset(idx)]; (std::forward(func)(elem), ...); return elem; } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given instance. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the object to construct. */ template void insert(It first, It last, const value_type& value = {}) { const auto cap = underlying_type::size() + std::distance(first, last); underlying_type::reserve(cap); assure_at_least(cap); for (; first != last; ++first) { push_at(underlying_type::size(), value); ENTT_TRY{ underlying_type::emplace_back(*first); } ENTT_CATCH{ pop_at(underlying_type::size()); ENTT_THROW; } } } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given range. * * @sa construct * * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of objects. */ template::value_type>, value_type>>> void insert(EIt first, EIt last, CIt from) { const auto cap = underlying_type::size() + std::distance(first, last); underlying_type::reserve(cap); assure_at_least(cap); for (; first != last; ++first, ++from) { push_at(underlying_type::size(), *from); ENTT_TRY{ underlying_type::emplace_back(*first); } ENTT_CATCH{ pop_at(underlying_type::size()); ENTT_THROW; } } } /** * @brief Sort elements according to the given comparison function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(const Entity, const Entity); * bool(const Type &, const Type &); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function oject must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @warning * Empty types are never instantiated. Therefore, only comparison function * objects that require to return entities rather than components are * accepted. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&... args) { if constexpr (std::is_invocable_v) { underlying_type::sort_n(length, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { const auto ilhs = underlying_type::index(lhs), irhs = underlying_type::index(rhs); return compare(std::as_const(packed[page(ilhs)][offset(ilhs)]), std::as_const(packed[page(irhs)][offset(irhs)])); }, std::move(algo), std::forward(args)...); } else { underlying_type::sort_n(length, std::move(compare), std::move(algo), std::forward(args)...); } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { sort_n(underlying_type::size(), std::move(compare), std::move(algo), std::forward(args)...); } private: typename alloc_traits::allocator_type allocator; typename bucket_alloc_traits::allocator_type bucket_allocator; bucket_alloc_pointer packed; size_type bucket; }; /*! @copydoc basic_storage_impl */ template class basic_storage_impl::ignore_if_empty::value&& std::is_empty_v>> : public basic_sparse_set::template rebind_alloc> { using comp_traits = component_traits; using underlying_type = basic_sparse_set::template rebind_alloc>; using alloc_traits = typename std::allocator_traits::template rebind_traits; public: /*! @brief Allocator type. */ using allocator_type = typename alloc_traits::allocator_type; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /** * @brief Default constructor. * @param alloc Allocator to use (possibly default-constructed). */ explicit basic_storage_impl(const allocator_type& alloc = {}) : underlying_type{ deletion_policy{comp_traits::in_place_delete::value}, alloc } {} /** * @brief Fake get function. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid entity identifier. */ void get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(underlying_type::contains(entt), "Storage does not contain entity"); } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to construct an object for the entity. */ template void emplace(const entity_type entt, Args &&... args) { [[maybe_unused]] value_type instance{ std::forward(args)... }; underlying_type::emplace(entt); } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. */ template void patch([[maybe_unused]] const entity_type entt, Func &&... func) { ENTT_ASSERT(underlying_type::contains(entt), "Storage does not contain entity"); (std::forward(func)(), ...); } /** * @brief Assigns one or more entities to a storage. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last, const value_type & = {}) { underlying_type::insert(first, last); } }; /** * @brief Mixin type to use to wrap basic storage classes. * @tparam Type The type of the underlying storage. */ template struct storage_adapter_mixin : Type { static_assert(std::is_same_v>, "Invalid object type"); /*! @brief Type of the objects assigned to entities. */ using value_type = typename Type::value_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Inherited constructors. */ using Type::Type; /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param entt A valid entity identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(basic_registry&, const entity_type entt, Args &&... args) { return Type::emplace(entt, std::forward(args)...); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(basic_registry&, It first, It last, Args &&... args) { Type::insert(first, last, std::forward(args)...); } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(basic_registry&, const entity_type entt, Func &&... func) { return Type::patch(entt, std::forward(func)...); } }; /** * @brief Mixin type to use to add signal support to storage types. * @tparam Type The type of the underlying storage. */ template class sigh_storage_mixin final : public Type { /*! @copydoc basic_sparse_set::swap_and_pop */ void swap_and_pop(const typename Type::entity_type entt, void* ud) final { ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry"); destruction.publish(*static_cast *>(ud), entt); Type::swap_and_pop(entt, ud); } /*! @copydoc basic_sparse_set::in_place_pop */ void in_place_pop(const typename Type::entity_type entt, void* ud) final { ENTT_ASSERT(ud != nullptr, "Invalid pointer to registry"); destruction.publish(*static_cast *>(ud), entt); Type::in_place_pop(entt, ud); } public: /*! @brief Underlying value type. */ using value_type = typename Type::value_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Inherited constructors. */ using Type::Type; /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever a new instance is created and assigned to an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **after** the object has been assigned to the * entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { return sink{ construction }; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **after** the object has been updated. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_update() ENTT_NOEXCEPT { return sink{ update }; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is removed from an entity and thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * Listeners are invoked **before** the object has been removed from the * entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { return sink{ destruction }; } /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param owner The registry that issued the request. * @param entt A valid entity identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(basic_registry& owner, const entity_type entt, Args &&... args) { Type::emplace(entt, std::forward(args)...); construction.publish(owner, entt); return this->get(entt); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param owner The registry that issued the request. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(basic_registry& owner, It first, It last, Args &&... args) { Type::insert(first, last, std::forward(args)...); if (!construction.empty()) { for (; first != last; ++first) { construction.publish(owner, *first); } } } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param owner The registry that issued the request. * @param entt A valid entity identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(basic_registry& owner, const entity_type entt, Func &&... func) { Type::patch(entt, std::forward(func)...); update.publish(owner, entt); return this->get(entt); } private: sigh&, const entity_type)> construction{}; sigh&, const entity_type)> destruction{}; sigh&, const entity_type)> update{}; }; /** * @brief Storage implementation dispatcher. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects assigned to the entities. * @tparam Allocator Type of allocator used to manage memory and elements. */ template struct basic_storage : basic_storage_impl { using basic_storage_impl::basic_storage_impl; }; /** * @brief Provides a common way to access certain properties of storage types. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Type Type of objects managed by the storage class. */ template struct storage_traits { /*! @brief Resulting type after component-to-storage conversion. */ using storage_type = sigh_storage_mixin>; }; /** * @brief Gets the element assigned to an entity from a storage, if any. * @tparam Type Storage type. * @param container A valid instance of a storage class. * @param entt A valid entity identifier. * @return A possibly empty tuple containing the requested element. */ template [[nodiscard]] auto get_as_tuple([[maybe_unused]] Type& container, [[maybe_unused]] const typename Type::entity_type entt) { static_assert(std::is_same_v, typename storage_traits::storage_type>, "Invalid storage"); if constexpr (std::is_void_v ) { return std::make_tuple(); } else { return std::forward_as_tuple(container.get(entt)); } } } #endif // #include "entity/utility.hpp" #ifndef ENTT_ENTITY_UTILITY_HPP #define ENTT_ENTITY_UTILITY_HPP // #include "../core/type_traits.hpp" namespace entt { /** * @brief Alias for exclusion lists. * @tparam Type List of types. */ template struct exclude_t : type_list {}; /** * @brief Variable template for exclusion lists. * @tparam Type List of types. */ template inline constexpr exclude_t exclude{}; /** * @brief Alias for lists of observed components. * @tparam Type List of types. */ template struct get_t : type_list {}; /** * @brief Variable template for lists of observed components. * @tparam Type List of types. */ template inline constexpr get_t get{}; } #endif // #include "entity/view.hpp" #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" // #include "utility.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class view_iterator final { using basic_common_type = basic_sparse_set::value_type>; [[nodiscard]] bool valid() const { const auto entt = *it; return Policy::accept(entt) && std::apply([entt](const auto *... curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *... curr) { return (!curr->contains(entt) && ...); }, filter); } public: using iterator_type = It; using difference_type = typename std::iterator_traits::difference_type; using value_type = typename std::iterator_traits::value_type; using pointer = typename std::iterator_traits::pointer; using reference = typename std::iterator_traits::reference; using iterator_category = std::bidirectional_iterator_tag; view_iterator() ENTT_NOEXCEPT : first{}, last{}, it{}, pools{}, filter{} {} view_iterator(It from, It to, It curr, std::array all_of, std::array none_of) ENTT_NOEXCEPT : first{ from }, last{ to }, it{ curr }, pools{ all_of }, filter{ none_of } { if (it != last && !valid()) { ++(*this); } } view_iterator& operator++() ENTT_NOEXCEPT { while (++it != last && !valid()); return *this; } view_iterator operator++(int) ENTT_NOEXCEPT { view_iterator orig = *this; return ++(*this), orig; } view_iterator& operator--() ENTT_NOEXCEPT { while (--it != first && !valid()); return *this; } view_iterator operator--(int) ENTT_NOEXCEPT { view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] bool operator==(const view_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const view_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } [[nodiscard]] pointer operator->() const { return &*it; } [[nodiscard]] reference operator*() const { return *operator->(); } private: It first; It last; It it; std::array pools; std::array filter; }; } /** * Internal details not to be documented. * @endcond */ /*! @brief Stable storage policy, aimed at pointer stability. */ struct stable_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity entity) ENTT_NOEXCEPT { return entity != tombstone; } /** * Internal details not to be documented. * @endcond */ }; /*! @brief Packed storage policy, aimed at faster linear iteration. */ struct packed_storage_policy { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template [[nodiscard]] static constexpr bool accept(const Entity) ENTT_NOEXCEPT { return true; } /** * Internal details not to be documented. * @endcond */ }; /** * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_view_impl; /*! @brief View implementation dispatcher. */ template struct basic_view; /** * @brief Multi component view. * * Multi component views iterate over those entities that have at least all the * given components in their bags. During initialization, a multi component view * looks at the number of entities available for each component and uses the * smallest set in order to get a performance boost when iterate. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Policy Common (stricter) storage policy. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template class basic_view_impl, Component...> { using basic_common_type = basic_sparse_set; template using storage_type = constness_as_t>::storage_type, Comp>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; iterable_iterator(It from, const basic_view_impl* parent) ENTT_NOEXCEPT : it{ from }, view{ parent } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return ++it, * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return std::tuple_cat(std::make_tuple(*it), view->get(*it)); } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: It it; const basic_view_impl* view; }; public: using iterator = iterable_iterator>; using reverse_iterator = iterable_iterator>; iterable(const basic_view_impl& parent) : view{ parent } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return { view.begin(), &view }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return { view.end(), &view }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return { view.rbegin(), &view }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return { view.rend(), &view }; } private: const basic_view_impl view; }; [[nodiscard]] const auto* candidate() const ENTT_NOEXCEPT { return (std::min)({ static_cast(std::get*>(pools))... }, [](const auto* lhs, const auto* rhs) { return lhs->size() < rhs->size(); }); } [[nodiscard]] auto pools_to_unchecked_array() const ENTT_NOEXCEPT { std::size_t pos{}; std::array other{}; (static_cast(std::get*>(pools) == view ? void() : void(other[pos++] = std::get*>(pools))), ...); return other; } [[nodiscard]] auto filter_to_array() const ENTT_NOEXCEPT { return std::array{std::get*>(filter)...}; } template [[nodiscard]] auto dispatch_get([[maybe_unused]] It& it, [[maybe_unused]] const Entity entt) const { if constexpr (std::is_same_v::value_type, typename storage_type::value_type>) { return std::forward_as_tuple(*it); } else { return get_as_tuple(*std::get*>(pools), entt); } } template void traverse(Func func) const { if constexpr (std::is_void_v *>(pools)->get({})) > ) { for (const auto entt : static_cast(*std::get*>(pools))) { if (Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } } else { auto it = std::get*>(pools)->begin(); for (const auto entt : static_cast(*std::get*>(pools))) { if (Policy::accept(entt) && ((std::is_same_v || std::get *>(pools)->contains(entt)) && ...) && (!std::get *>(filter)->contains(entt) && ...)) { if constexpr (is_applicable_v < Func, decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))) > ) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); } else { std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); } } ++it; } } } public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param component The storage for the types to iterate. * @param epool The storage for the types used to filter the view. */ basic_view_impl(storage_type &... component, const storage_type &... epool) ENTT_NOEXCEPT : pools{ &component... }, filter{ &epool... }, view{ candidate() } {} /** * @brief Forces the type to use to drive iterations. * @tparam Comp Type of component to use to drive the iteration. */ template void use() const ENTT_NOEXCEPT { view = std::get*>(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const ENTT_NOEXCEPT { return view->size(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const { return iterator{ view->begin(), view->end(), view->begin(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const { return iterator{ view->begin(), view->end(), view->end(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const { return reverse_iterator{ view->rbegin(), view->rend(), view->rbegin(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const { return reverse_iterator{ view->rbegin(), view->rend(), view->rend(), pools_to_unchecked_array(), filter_to_array() }; } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = iterator{ view->begin(), view->end(), view->find(entt), pools_to_unchecked_array(), filter_to_array() }; return (it != end() && *it == entt) ? it : end(); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return view != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return (std::get*>(pools)->contains(entt) && ...) && (!std::get*>(filter)->contains(entt) && ...); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr (sizeof...(Comp) == 0) { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } else if constexpr (sizeof...(Comp) == 1) { return (std::get*>(pools)->get(entt), ...); } else { return std::tuple_cat(get_as_tuple(*std::get*>(pools), entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { ((std::get*>(pools) == view ? traverse(std::move(func)) : void()), ...); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The pool of the suggested component is used to lead the iterations. The * returned entities will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { use(); traverse(std::move(func)); } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{ *this }; } /** * @brief Returns an iterable object to use to _visit_ the view. * * The pool of the suggested component is used to lead the iterations. The * returned elements will therefore respect the order of the pool associated * with that type. * * @sa each * * @tparam Comp Type of component to use to drive the iteration. * @return An iterable object to use to _visit_ the view. */ template [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { use(); return iterable_view{ *this }; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...>&, const basic_view, CRhs...>&); private: const std::tuple *...> pools; const std::tuple *...> filter; mutable const basic_common_type* view; }; /** * @brief Single component view specialization. * * Single component views are specialized in order to get a boost in terms of * performance. This kind of views can access the underlying data structure * directly and avoid superfluous checks. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given component are created and assigned to entities. * * The entity currently pointed is modified (as an example, the given * component is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @note * Views share a reference to the underlying data structure of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by views. * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Component Type of component iterated by the view. */ template class basic_view_impl, Component> { using basic_common_type = basic_sparse_set; using storage_type = constness_as_t>::storage_type, Component>; class iterable final { template struct iterable_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; template iterable_iterator(It... from, Discard...) ENTT_NOEXCEPT : it{ from... } {} iterable_iterator& operator++() ENTT_NOEXCEPT { return (++std::get(it), ...), * this; } iterable_iterator operator++(int) ENTT_NOEXCEPT { iterable_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return { *std::get(it)... }; } [[nodiscard]] bool operator==(const iterable_iterator& other) const ENTT_NOEXCEPT { return std::get<0>(other.it) == std::get<0>(it); } [[nodiscard]] bool operator!=(const iterable_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: std::tuple it; }; public: using iterator = std::conditional_t < std::is_void_v().get({})) > , iterable_iterator, iterable_iterator().begin())> > ; using reverse_iterator = std::conditional_t < std::is_void_v().get({})) > , iterable_iterator, iterable_iterator().rbegin())> > ; iterable(storage_type& ref) : pool{ &ref } {} [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ pool->basic_common_type::begin(), pool->begin() }; } [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{ pool->basic_common_type::end(), pool->end() }; } [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return reverse_iterator{ pool->basic_common_type::rbegin(), pool->rbegin() }; } [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return reverse_iterator{ pool->basic_common_type::rend(), pool->rend() }; } private: storage_type* const pool; }; public: /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename basic_common_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename basic_common_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable_view = iterable; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view_impl() ENTT_NOEXCEPT : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view_impl(storage_type& ref) ENTT_NOEXCEPT : pools{ &ref }, filter{} {} /** * @brief Returns the number of entities that have the given component. * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return std::get<0>(pools)->size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return std::get<0>(pools)->empty(); } /** * @brief Direct access to the raw representation offered by the storage. * @return A pointer to the array of components. */ [[nodiscard]] auto raw() const ENTT_NOEXCEPT { return std::get<0>(pools)->raw(); } /** * @brief Direct access to the list of entities. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @return A pointer to the array of entities. */ [[nodiscard]] auto data() const ENTT_NOEXCEPT { return std::get<0>(pools)->data(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::begin(); } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::end(); } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::rbegin(); } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { return std::get<0>(pools)->basic_common_type::rend(); } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid entity identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const { const auto it = std::get<0>(pools)->find(entt); return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return std::get<0>(pools) != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return std::get<0>(pools)->contains(entt); } /** * @brief Returns the component assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the view * results in undefined behavior. * * @tparam Comp Types of components to get. * @param entt A valid entity identifier. * @return The component assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { ENTT_ASSERT(contains(entt), "View does not contain entity"); if constexpr (sizeof...(Comp) == 0) { return get_as_tuple(*std::get<0>(pools), entt); } else { static_assert(std::is_same_v, "Invalid component type"); return std::get<0>(pools)->get(entt); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a reference to the component if it's a non-empty one. * The _constness_ of the component is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Component &); * void(Component &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if constexpr (std::is_void_v(pools)->get({})) > ) { if constexpr (std::is_invocable_v) { for (auto pos = size(); pos; --pos) { func(); } } else { for (auto entity : *this) { func(entity); } } } else { if constexpr (is_applicable_v) { for (const auto pack : each()) { std::apply(func, pack); } } else { for (auto&& component : *std::get<0>(pools)) { func(component); } } } } /** * @brief Returns an iterable object to use to _visit_ the view. * * The iterable object returns tuples that contain the current entity and a * reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable_view each() const ENTT_NOEXCEPT { return iterable_view{ *std::get<0>(pools) }; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam Id A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @return A more specific view. */ template friend auto operator|(const basic_view, CLhs...>&, const basic_view, CRhs...>&); private: const std::tuple pools; const std::tuple<> filter; }; /** * @brief View implementation dispatcher. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Exclude Types of components used to filter the view. * @tparam Component Types of components iterated by the view. */ template struct basic_view, Component...> : basic_view_impl>::in_place_delete...>, stable_storage_policy, packed_storage_policy>, Entity, exclude_t, Component...> { /*! @brief Most restrictive storage policy of all component types. */ using storage_policy = std::conditional_t>::in_place_delete...>, stable_storage_policy, packed_storage_policy>; using basic_view_impl, Component...>::basic_view_impl; }; /** * @brief Deduction guide. * @tparam Storage Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Storage &... storage) ->basic_view, entt::exclude_t<>, constness_as_t...>; /** * @brief Combines two views in a _more specific_ one. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam ELhs Filter list of the first view. * @tparam CLhs Component list of the first view. * @tparam ERhs Filter list of the second view. * @tparam CRhs Component list of the second view. * @param lhs A valid reference to the first view. * @param rhs A valid reference to the second view. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, CLhs...>& lhs, const basic_view, CRhs...>& rhs) { using view_type = basic_view, CLhs..., CRhs...>; return std::apply([](auto *... storage) { return view_type{ *storage... }; }, std::tuple_cat(lhs.pools, rhs.pools, lhs.filter, rhs.filter)); } } #endif // #include "locator/locator.hpp" #ifndef ENTT_LOCATOR_LOCATOR_HPP #define ENTT_LOCATOR_LOCATOR_HPP #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /** * @brief Service locator, nothing more. * * A service locator can be used to do what it promises: locate services.
* Usually service locators are tightly bound to the services they expose and * thus it's hard to define a general purpose class to do that. This template * based implementation tries to fill the gap and to get rid of the burden of * defining a different specific locator for each application. * * @tparam Service Type of service managed by the locator. */ template struct service_locator { /*! @brief Type of service offered. */ using service_type = Service; /*! @brief Default constructor, deleted on purpose. */ service_locator() = delete; /*! @brief Default destructor, deleted on purpose. */ ~service_locator() = delete; /** * @brief Tests if a valid service implementation is set. * @return True if the service is set, false otherwise. */ [[nodiscard]] static bool empty() ENTT_NOEXCEPT { return !static_cast(service); } /** * @brief Returns a weak pointer to a service implementation, if any. * * Clients of a service shouldn't retain references to it. The recommended * way is to retrieve the service implementation currently set each and * every time the need of using it arises. Otherwise users can incur in * unexpected behaviors. * * @return A reference to the service implementation currently set, if any. */ [[nodiscard]] static std::weak_ptr get() ENTT_NOEXCEPT { return service; } /** * @brief Returns a weak reference to a service implementation, if any. * * Clients of a service shouldn't retain references to it. The recommended * way is to retrieve the service implementation currently set each and * every time the need of using it arises. Otherwise users can incur in * unexpected behaviors. * * @warning * In case no service implementation has been set, a call to this function * results in undefined behavior. * * @return A reference to the service implementation currently set, if any. */ [[nodiscard]] static Service& ref() ENTT_NOEXCEPT { return *service; } /** * @brief Sets or replaces a service. * @tparam Impl Type of the new service to use. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. */ template static void set(Args &&... args) { service = std::make_shared(std::forward(args)...); } /** * @brief Sets or replaces a service. * @param ptr Service to use to replace the current one. */ static void set(std::shared_ptr ptr) { ENTT_ASSERT(static_cast(ptr), "Null service not allowed"); service = std::move(ptr); } /** * @brief Resets a service. * * The service is no longer valid after a reset. */ static void reset() { service.reset(); } private: inline static std::shared_ptr service = nullptr; }; } #endif // #include "meta/adl_pointer.hpp" #ifndef ENTT_META_ADL_POINTER_HPP #define ENTT_META_ADL_POINTER_HPP namespace entt { /** * @brief ADL based lookup function for dereferencing meta pointer-like types. * @tparam Type Element type. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ template decltype(auto) dereference_meta_pointer_like(const Type& value) { return *value; } /** * @brief Fake ADL based lookup function for meta pointer-like types. * @tparam Type Element type. */ template struct adl_meta_pointer_like { /** * @brief Uses the default ADL based lookup method to resolve the call. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ static decltype(auto) dereference(const Type& value) { return dereference_meta_pointer_like(value); } }; } #endif // #include "meta/container.hpp" #ifndef ENTT_META_CONTAINER_HPP #define ENTT_META_CONTAINER_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "type_traits.hpp" #ifndef ENTT_META_TYPE_TRAITS_HPP #define ENTT_META_TYPE_TRAITS_HPP #include namespace entt { /** * @brief Traits class template to be specialized to enable support for meta * template information. */ template struct meta_template_traits; /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. */ template struct meta_sequence_container_traits; /** * @brief Traits class template to be specialized to enable support for meta * associative containers. */ template struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a meta associative * container claims to wrap a key-only type, false otherwise. * @tparam Type Potentially key-only meta associative container type. */ template struct is_key_only_meta_associative_container : std::true_type {}; /*! @copydoc is_key_only_meta_associative_container */ template struct is_key_only_meta_associative_container::type::mapped_type>> : std::false_type {}; /** * @brief Helper variable template. * @tparam Type Potentially key-only meta associative container type. */ template inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container::value; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like : std::false_type {}; /** * @brief Partial specialization to ensure that const pointer-like types are * also accepted. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like : is_meta_pointer_like {}; /** * @brief Helper variable template. * @tparam Type Potentially pointer-like type. */ template inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; } #endif namespace entt { /** * @brief Container traits. * @tparam Container Type of the underlying container. * @tparam Trait Traits associated with the underlying container. */ template class... Trait> struct meta_container_traits : public Trait... { /*! @brief Type of container. */ using type = Container; }; /** * @brief Basic STL-compatible container traits * @tparam Container The type of the container. */ template struct basic_container { /** * @brief Returns the size of the given container. * @param cont The container for which to return the size. * @return The size of the given container. */ [[nodiscard]] static typename Container::size_type size(const Container& cont) ENTT_NOEXCEPT { return cont.size(); } /** * @brief Returns an iterator to the first element of the given container. * @param cont The container for which to return the iterator. * @return An iterator to the first element of the given container. */ [[nodiscard]] static typename Container::iterator begin(Container& cont) { return cont.begin(); } /** * @brief Returns an iterator to the first element of the given container. * @param cont The container for which to return the iterator. * @return An iterator to the first element of the given container. */ [[nodiscard]] static typename Container::const_iterator cbegin(const Container& cont) { return cont.begin(); } /** * @brief Returns an iterator past the last element of the given container. * @param cont The container for which to return the iterator. * @return An iterator past the last element of the given container. */ [[nodiscard]] static typename Container::iterator end(Container& cont) { return cont.end(); } /** * @brief Returns an iterator past the last element of the given container. * @param cont The container for which to return the iterator. * @return An iterator past the last element of the given container. */ [[nodiscard]] static typename Container::const_iterator cend(const Container& cont) { return cont.end(); } }; /** * @brief Basic STL-compatible associative container traits * @tparam Container The type of the container. */ template struct basic_associative_container { /** * @brief Returns an iterator to the element with key equivalent to the * given one, if any. * @param cont The container in which to search for the element. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] static typename Container::iterator find(Container& cont, const typename Container::key_type& key) { return cont.find(key); } /*! @copydoc find */ [[nodiscard]] static typename Container::const_iterator cfind(const Container& cont, const typename Container::key_type& key) { return cont.find(key); } }; /** * @brief Basic STL-compatible dynamic container traits * @tparam Container The type of the container. */ template struct basic_dynamic_container { /** * @brief Clears the content of the given container. * @param cont The container for which to clear the content. * @return True in case of success, false otherwise. */ [[nodiscard]] static bool clear([[maybe_unused]] Container& cont) { return cont.clear(), true; } }; /** * @brief Basic STL-compatible dynamic associative container traits * @tparam Container The type of the container. */ template struct basic_dynamic_associative_container { /** * @brief Removes the specified element from the given container. * @param cont The container from which to remove the element. * @param key The element to remove. * @return A bool denoting whether the removal took place. */ [[nodiscard]] static bool erase([[maybe_unused]] Container& cont, [[maybe_unused]] const typename Container::key_type& key) { const auto sz = cont.size(); return cont.erase(key) != sz; } }; /** * @brief Basic STL-compatible sequence container traits * @tparam Container The type of the container. */ template struct basic_sequence_container { /** * @brief Returns a reference to the element at the specified location of * the given container (no bounds checking is performed). * @param cont The container from which to get the element. * @param pos The position of the element to return. * @return A reference to the requested element. */ [[nodiscard]] static typename Container::reference get(Container& cont, typename Container::size_type pos) { return cont[pos]; } /*! @copydoc get */ [[nodiscard]] static typename Container::const_reference cget(const Container& cont, typename Container::size_type pos) { return cont[pos]; } }; /** * @brief STL-compatible dynamic associative key-only container traits * @tparam Container The type of the container. */ template struct dynamic_associative_key_only_container { /** * @brief Inserts an element into the given container. * @param cont The container in which to insert the element. * @param key The element to insert. * @return A bool denoting whether the insertion took place. */ [[nodiscard]] static bool insert([[maybe_unused]] Container& cont, [[maybe_unused]] const typename Container::key_type& key) { return cont.insert(key).second; } }; /** * @brief STL-compatible dynamic key-value associative container traits * @tparam Container The type of the container. */ template struct dynamic_associative_key_value_container { /** * @brief Inserts an element (a key/value pair) into the given container. * @param cont The container in which to insert the element. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ [[nodiscard]] static bool insert([[maybe_unused]] Container& cont, [[maybe_unused]] const typename Container::key_type& key, [[maybe_unused]] const typename Container::mapped_type& value) { return cont.insert(std::make_pair(key, value)).second; } }; /** * @brief STL-compatible dynamic sequence container traits * @tparam Container The type of the container. */ template struct dynamic_sequence_container { /** * @brief Resizes the given container to contain the given number of * elements. * @param cont The container to resize. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ [[nodiscard]] static bool resize([[maybe_unused]] Container& cont, [[maybe_unused]] typename Container::size_type sz) { return cont.resize(sz), true; } /** * @brief Inserts an element at the specified location of the given * container. * @param cont The container into which to insert the element. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A pair consisting of an iterator to the inserted element (in case * of success) and a bool denoting whether the insertion took place. */ [[nodiscard]] static std::pair insert([[maybe_unused]] Container& cont, [[maybe_unused]] typename Container::const_iterator it, [[maybe_unused]] const typename Container::value_type& value) { return { cont.insert(it, value), true }; } /** * @brief Removes the element at the specified location from the given * container. * @param cont The container from which to remove the element. * @param it Iterator to the element to remove. * @return A pair consisting of an iterator following the last removed * element (in case of success) and a bool denoting whether the insertion * took place. */ [[nodiscard]] static std::pair erase([[maybe_unused]] Container& cont, [[maybe_unused]] typename Container::const_iterator it) { return { cont.erase(it), true }; } }; /** * @brief STL-compatible fixed sequence container traits * @tparam Container The type of the container. */ template struct fixed_sequence_container { /** * @brief Does nothing. * @return False to indicate failure in all cases. */ [[nodiscard]] static bool resize(const Container&, typename Container::size_type) { return false; } /** * @brief Does nothing. * @return False to indicate failure in all cases. */ [[nodiscard]] static bool clear(const Container&) { return false; } /** * @brief Does nothing. * @return A pair consisting of an invalid iterator and a false value to * indicate failure in all cases. */ [[nodiscard]] static std::pair insert(const Container&, typename Container::const_iterator, const typename Container::value_type&) { return { {}, false }; } /** * @brief Does nothing. * @return A pair consisting of an invalid iterator and a false value to * indicate failure in all cases. */ [[nodiscard]] static std::pair erase(const Container&, typename Container::const_iterator) { return { {}, false }; } }; /** * @brief Meta sequence container traits for `std::vector`s of any type. * @tparam Type The type of elements. * @tparam Args Other arguments. */ template struct meta_sequence_container_traits> : meta_container_traits< std::vector, basic_container, basic_dynamic_container, basic_sequence_container, dynamic_sequence_container > {}; /** * @brief Meta sequence container traits for `std::array`s of any type. * @tparam Type The type of elements. * @tparam N The number of elements. */ template struct meta_sequence_container_traits> : meta_container_traits< std::array, basic_container, basic_sequence_container, fixed_sequence_container > {}; /** * @brief Meta associative container traits for `std::map`s of any type. * @tparam Key The key type of elements. * @tparam Value The value type of elements. * @tparam Args Other arguments. */ template struct meta_associative_container_traits> : meta_container_traits< std::map, basic_container, basic_associative_container, basic_dynamic_container, basic_dynamic_associative_container, dynamic_associative_key_value_container > {}; /** * @brief Meta associative container traits for `std::unordered_map`s of any * type. * @tparam Key The key type of elements. * @tparam Value The value type of elements. * @tparam Args Other arguments. */ template struct meta_associative_container_traits> : meta_container_traits< std::unordered_map, basic_container, basic_associative_container, basic_dynamic_container, basic_dynamic_associative_container, dynamic_associative_key_value_container > {}; /** * @brief Meta associative container traits for `std::set`s of any type. * @tparam Key The type of elements. * @tparam Args Other arguments. */ template struct meta_associative_container_traits> : meta_container_traits< std::set, basic_container, basic_associative_container, basic_dynamic_container, basic_dynamic_associative_container, dynamic_associative_key_only_container > {}; /** * @brief Meta associative container traits for `std::unordered_set`s of any * type. * @tparam Key The type of elements. * @tparam Args Other arguments. */ template struct meta_associative_container_traits> : meta_container_traits< std::unordered_set, basic_container, basic_associative_container, basic_dynamic_container, basic_dynamic_associative_container, dynamic_associative_key_only_container > {}; } #endif // #include "meta/ctx.hpp" #ifndef ENTT_META_CTX_HPP #define ENTT_META_CTX_HPP // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct ENTT_API meta_context { // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ // inline static meta_type_node *local = nullptr; // inline static meta_type_node **global = &local; [[nodiscard]] static meta_type_node*& local() ENTT_NOEXCEPT { static meta_type_node* chain = nullptr; return chain; } [[nodiscard]] static meta_type_node**& global() ENTT_NOEXCEPT { static meta_type_node** chain = &local(); return chain; } }; } /** * Internal details not to be documented. * @endcond */ /*! @brief Opaque container for a meta context. */ struct meta_ctx { /** * @brief Binds the meta system to a given context. * @param other A valid context to which to bind. */ static void bind(meta_ctx other) ENTT_NOEXCEPT { internal::meta_context::global() = other.ctx; } private: internal::meta_type_node** ctx{ &internal::meta_context::local() }; }; } #endif // #include "meta/factory.hpp" #ifndef ENTT_META_FACTORY_HPP #define ENTT_META_FACTORY_HPP #include #include #include #include // #include "../config/config.h" // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "../core/type_traits.hpp" // #include "meta.hpp" #ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "fwd.hpp" // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "hashed_string.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif namespace entt { /** * @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 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; using vtable_type = const void* (const operation, const basic_any&, void*); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template [[nodiscard]] static constexpr policy type_to_policy() { if constexpr (std::is_lvalue_reference_v) { if constexpr (std::is_const_v>) { return policy::CREF; } else { return policy::REF; } } else { return policy::OWNER; } } template [[nodiscard]] static bool compare(const void* lhs, const void* rhs) { if constexpr (!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); } else { return lhs == rhs; } } template 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>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { const Type* instance = (in_situ && from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); switch (op) { case operation::COPY: if constexpr (std::is_copy_constructible_v) { static_cast(to)->emplace(*instance); } break; case operation::MOVE: if constexpr (in_situ) { if (from.mode == policy::OWNER) { return new (&static_cast(to)->storage) Type{ std::move(*const_cast(instance)) }; } } return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); case operation::DTOR: if (from.mode == policy::OWNER) { if constexpr (in_situ) { instance->~Type(); } else if constexpr (std::is_array_v) { delete[] instance; } else { delete instance; } } break; case operation::COMP: return compare(instance, (*static_cast(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(to) = type_id(); break; } } return nullptr; } template void initialize([[maybe_unused]] Args &&... args) { if constexpr (!std::is_void_v) { if constexpr (std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); instance = (std::addressof(args), ...); } else if constexpr (in_situ) { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { new (&storage) Type{ std::forward(args)... }; } else { new (&storage) Type(std::forward(args)...); } } else { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { instance = new Type{ std::forward(args)... }; } else { instance = new Type(std::forward(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 }, 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 explicit basic_any(std::in_place_type_t, Args &&... args) : instance{}, vtable{ &basic_vtable>> }, mode{ type_to_policy() } { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper 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 basic_any(std::reference_wrapper value) ENTT_NOEXCEPT : basic_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, basic_any>>> basic_any(Type&& value) : instance{}, vtable{ &basic_vtable> }, mode{ policy::OWNER } { initialize>(std::forward(value)); } /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any& other) : instance{}, vtable{ &basic_vtable }, 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 [[deprecated("Use std::in_place_type, entt::make_any, emplace or forward_as_any instead")]] basic_any& operator=(std::reference_wrapper value) ENTT_NOEXCEPT { emplace(value.get()); 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 std::enable_if_t, basic_any>, basic_any&> operator=(Type&& value) { emplace>(std::forward(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(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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, *this, nullptr); mode = type_to_policy(); initialize(std::forward(args)...); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(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; }; /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ template [[nodiscard]] inline bool operator!=(const basic_any& lhs, const basic_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @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 Type any_cast(const basic_any& data) ENTT_NOEXCEPT { const auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any&& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } /*! @copydoc any_cast */ template const Type* any_cast(const basic_any* data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ template Type* any_cast(basic_any* data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } /** * @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::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&... args) { return basic_any{std::in_place_type, std::forward(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::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type&& value) { return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } } #endif // #include "../core/fwd.hpp" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "adl_pointer.hpp" #ifndef ENTT_META_ADL_POINTER_HPP #define ENTT_META_ADL_POINTER_HPP namespace entt { /** * @brief ADL based lookup function for dereferencing meta pointer-like types. * @tparam Type Element type. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ template decltype(auto) dereference_meta_pointer_like(const Type& value) { return *value; } /** * @brief Fake ADL based lookup function for meta pointer-like types. * @tparam Type Element type. */ template struct adl_meta_pointer_like { /** * @brief Uses the default ADL based lookup method to resolve the call. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ static decltype(auto) dereference(const Type& value) { return dereference_meta_pointer_like(value); } }; } #endif // #include "ctx.hpp" #ifndef ENTT_META_CTX_HPP #define ENTT_META_CTX_HPP // #include "../core/attribute.h" // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct ENTT_API meta_context { // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ // inline static meta_type_node *local = nullptr; // inline static meta_type_node **global = &local; [[nodiscard]] static meta_type_node*& local() ENTT_NOEXCEPT { static meta_type_node* chain = nullptr; return chain; } [[nodiscard]] static meta_type_node**& global() ENTT_NOEXCEPT { static meta_type_node** chain = &local(); return chain; } }; } /** * Internal details not to be documented. * @endcond */ /*! @brief Opaque container for a meta context. */ struct meta_ctx { /** * @brief Binds the meta system to a given context. * @param other A valid context to which to bind. */ static void bind(meta_ctx other) ENTT_NOEXCEPT { internal::meta_context::global() = other.ctx; } private: internal::meta_type_node** ctx{ &internal::meta_context::local() }; }; } #endif // #include "node.hpp" #ifndef ENTT_META_NODE_HPP #define ENTT_META_NODE_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; struct meta_handle; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct meta_prop_node { meta_prop_node* next; const meta_any& id; meta_any& value; }; struct meta_base_node { meta_type_node* const parent; meta_base_node* next; meta_type_node* (* const type)() ENTT_NOEXCEPT; const void* (* const cast)(const void*) ENTT_NOEXCEPT; }; struct meta_conv_node { meta_type_node* const parent; meta_conv_node* next; meta_type_node* (* const type)() ENTT_NOEXCEPT; meta_any(* const conv)(const void*); }; struct meta_ctor_node { using size_type = std::size_t; meta_type_node* const parent; meta_ctor_node* next; meta_prop_node* prop; const size_type arity; meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_any* const); }; struct meta_data_node { id_type id; meta_type_node* const parent; meta_data_node* next; meta_prop_node* prop; const bool is_const; const bool is_static; meta_type_node* (* const type)() ENTT_NOEXCEPT; bool(* const set)(meta_handle, meta_any); meta_any(* const get)(meta_handle); }; struct meta_func_node { using size_type = std::size_t; id_type id; meta_type_node* const parent; meta_func_node* next; meta_prop_node* prop; const size_type arity; const bool is_const; const bool is_static; meta_type_node* (* const ret)() ENTT_NOEXCEPT; meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_handle, meta_any*); }; struct meta_template_info { using size_type = std::size_t; const bool is_template_specialization; const size_type arity; meta_type_node* (* const type)() ENTT_NOEXCEPT; meta_type_node* (* const arg)(const size_type) ENTT_NOEXCEPT; }; struct meta_type_node { using size_type = std::size_t; const type_info info; id_type id; meta_type_node* next; meta_prop_node* prop; const size_type size_of; const bool is_void; const bool is_integral; const bool is_floating_point; const bool is_array; const bool is_enum; const bool is_union; const bool is_class; const bool is_pointer; const bool is_function_pointer; const bool is_member_object_pointer; const bool is_member_function_pointer; const bool is_pointer_like; const bool is_sequence_container; const bool is_associative_container; const meta_template_info template_info; const size_type rank; size_type(* const extent)(const size_type) ENTT_NOEXCEPT; meta_type_node* (* const remove_pointer)() ENTT_NOEXCEPT; meta_type_node* (* const remove_extent)() ENTT_NOEXCEPT; meta_ctor_node* const def_ctor; meta_ctor_node* ctor{ nullptr }; meta_base_node* base{ nullptr }; meta_conv_node* conv{ nullptr }; meta_data_node* data{ nullptr }; meta_func_node* func{ nullptr }; void(*dtor)(void*) { nullptr }; }; template auto meta_visit(const Op& op, const Node* node) -> std::decay_t*Member)> { for (auto* curr = node->*Member; curr; curr = curr->next) { if (op(curr)) { return curr; } } if constexpr (std::is_same_v) { for (auto* curr = node->base; curr; curr = curr->next) { if (auto* ret = meta_visit(op, curr->type()); ret) { return ret; } } } return nullptr; } template meta_type_node* meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; template class ENTT_API meta_node { static_assert(std::is_same_v>>, "Invalid type"); template [[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence) ENTT_NOEXCEPT { meta_type_node::size_type ext{}; ((ext = (dim == Index ? std::extent_v : ext)), ...); return ext; } [[nodiscard]] static meta_ctor_node* meta_default_constructor([[maybe_unused]] meta_type_node* type) ENTT_NOEXCEPT { if constexpr (std::is_default_constructible_v) { static meta_ctor_node node{ type, nullptr, nullptr, 0u, nullptr, [](meta_any* const) { return meta_any{std::in_place_type}; } }; return &node; } else { return nullptr; } } [[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT { if constexpr (is_complete_v>) { return { true, meta_template_traits::args_type::size, &meta_node::class_type>::resolve, [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } }; } else { return { false, 0u, nullptr, nullptr }; } } public: [[nodiscard]] static meta_type_node* resolve() ENTT_NOEXCEPT { static meta_type_node node{ type_id(), {}, nullptr, nullptr, size_of_v, std::is_void_v, std::is_integral_v, std::is_floating_point_v, std::is_array_v, std::is_enum_v, std::is_union_v, std::is_class_v, std::is_pointer_v, std::is_pointer_v && std::is_function_v>, std::is_member_object_pointer_v, std::is_member_function_pointer_v, is_meta_pointer_like_v, is_complete_v>, is_complete_v>, meta_template_descriptor(), std::rank_v, [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence>{}); }, & meta_node>>>::resolve, & meta_node>>>::resolve, meta_default_constructor(&node), meta_default_constructor(&node) }; return &node; } }; template struct meta_info : meta_node>> {}; template meta_type_node* meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { meta_type_node* args[sizeof...(Args) + 1u]{ nullptr, internal::meta_info::resolve()... }; return args[index + 1u]; } } /** * Internal details not to be documented. * @endcond */ } #endif // #include "range.hpp" #ifndef ENTT_META_RANGE_HPP #define ENTT_META_RANGE_HPP #include #include namespace entt { /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. * @tparam Node Type of meta nodes iterated. */ template class meta_range { struct range_iterator { using difference_type = std::ptrdiff_t; using value_type = Type; using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; using node_type = Node; range_iterator() ENTT_NOEXCEPT = default; range_iterator(node_type* head) ENTT_NOEXCEPT : it{ head } {} range_iterator& operator++() ENTT_NOEXCEPT { return (it = it->next), * this; } range_iterator operator++(int) ENTT_NOEXCEPT { range_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return it; } [[nodiscard]] bool operator==(const range_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const range_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: node_type* it{}; }; public: /*! @brief Node type. */ using node_type = Node; /*! @brief Input iterator type. */ using iterator = range_iterator; /*! @brief Default constructor. */ meta_range() ENTT_NOEXCEPT = default; /** * @brief Constructs a meta range from a given node. * @param head The underlying node with which to construct the range. */ meta_range(node_type* head) : node{ head } {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first meta object of the range. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ node }; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last meta object of the * range. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{}; } private: node_type* node{ nullptr }; }; } #endif // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; /*! @brief Proxy object for sequence containers. */ class meta_sequence_container { template struct meta_sequence_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_sequence_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for sequence containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : value_type_fn{ &meta_sequence_container_proxy::value_type }, size_fn{ &meta_sequence_container_proxy::size }, resize_fn{ &meta_sequence_container_proxy::resize }, clear_fn{ &meta_sequence_container_proxy::clear }, begin_fn{ &meta_sequence_container_proxy::begin }, end_fn{ &meta_sequence_container_proxy::end }, insert_fn{ &meta_sequence_container_proxy::insert }, erase_fn{ &meta_sequence_container_proxy::erase }, get_fn{ &meta_sequence_container_proxy::get }, storage{ std::move(instance) } {} [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool resize(size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline std::pair insert(iterator, meta_any); inline std::pair erase(iterator); [[nodiscard]] inline meta_any operator[](size_type); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: meta_type(*value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(*size_fn)(const any&) ENTT_NOEXCEPT = nullptr; bool(*resize_fn)(any&, size_type) = nullptr; bool(*clear_fn)(any&) = nullptr; iterator(*begin_fn)(any&) = nullptr; iterator(*end_fn)(any&) = nullptr; std::pair(*insert_fn)(any&, iterator, meta_any&) = nullptr; std::pair(*erase_fn)(any&, iterator) = nullptr; meta_any(*get_fn)(any&, size_type) = nullptr; any storage{}; }; /*! @brief Proxy object for associative containers. */ class meta_associative_container { template struct meta_associative_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_associative_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for associative containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : key_only_container{ is_key_only_meta_associative_container_v }, key_type_fn{ &meta_associative_container_proxy::key_type }, mapped_type_fn{ &meta_associative_container_proxy::mapped_type }, value_type_fn{ &meta_associative_container_proxy::value_type }, size_fn{ &meta_associative_container_proxy::size }, clear_fn{ &meta_associative_container_proxy::clear }, begin_fn{ &meta_associative_container_proxy::begin }, end_fn{ &meta_associative_container_proxy::end }, insert_fn{ &meta_associative_container_proxy::insert }, erase_fn{ &meta_associative_container_proxy::erase }, find_fn{ &meta_associative_container_proxy::find }, storage{ std::move(instance) } {} [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline bool insert(meta_any, meta_any); inline bool erase(meta_any); [[nodiscard]] inline iterator find(meta_any); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: bool key_only_container{}; meta_type(*key_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(*mapped_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(*value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(*size_fn)(const any&) ENTT_NOEXCEPT = nullptr; bool(*clear_fn)(any&) = nullptr; iterator(*begin_fn)(any&) = nullptr; iterator(*end_fn)(any&) = nullptr; bool(*insert_fn)(any&, meta_any&, meta_any&) = nullptr; bool(*erase_fn)(any&, meta_any&) = nullptr; iterator(*find_fn)(any&, meta_any&) = nullptr; any storage{}; }; /*! @brief Opaque wrapper for values of any type. */ class meta_any { enum class operation { DTOR, DEREF, SEQ, ASSOC }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any& from, [[maybe_unused]] void* to) { static_assert(std::is_same_v>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { switch (op) { case operation::DTOR: if (auto* curr = static_cast(to); curr->dtor && from.owner()) { curr->dtor(const_cast(from).data()); } break; case operation::DEREF: if constexpr (is_meta_pointer_like_v) { using element_type = std::remove_const_t::element_type>; if constexpr (std::is_function_v) { *static_cast(to) = any_cast(from); } else if constexpr (!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(from))); static_cast(to)->emplace(adl_meta_pointer_like::dereference(any_cast(from))); } } case operation::SEQ: if constexpr (is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; case operation::ASSOC: if constexpr (is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; } } } 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 } {} public: /*! @brief Default constructor. */ meta_any() ENTT_NOEXCEPT : storage{}, node{}, vtable{ &basic_vtable } {} /** * @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 explicit meta_any(std::in_place_type_t, Args &&... args) : storage{ std::in_place_type, std::forward(args)... }, node{ internal::meta_info::resolve() }, vtable{ &basic_vtable>> } {} /** * @brief Constructs a wrapper 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 meta_any(std::reference_wrapper value) : meta_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, meta_any>>> meta_any(Type&& value) : storage{ std::forward(value) }, node{ internal::meta_info>::resolve() }, vtable{ &basic_vtable> } {} /** * @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) } {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { vtable(operation::DTOR, storage, node); } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This meta any object. */ meta_any& operator=(const meta_any& other) { std::exchange(vtable, other.vtable)(operation::DTOR, storage, node); 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 { std::exchange(vtable, std::exchange(other.vtable, &basic_vtable))(operation::DTOR, storage, node); 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 [[deprecated("Use std::in_place_type, entt::make_meta, emplace or forward_as_meta instead")]] meta_any& operator=(std::reference_wrapper value) { emplace(value.get()); 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 std::enable_if_t, meta_any>, meta_any&> operator=(Type&& value) { emplace>(std::forward(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 meta_any invoke(const id_type id, Args &&... args) const; /*! @copydoc invoke */ template 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 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 [[nodiscard]] const Type* try_cast() const { if (node) { if (const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if (const auto* base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto* curr) { return curr->type()->info == info; }, node); base) { return static_cast(base->cast(storage.data())); } } return nullptr; } /*! @copydoc try_cast */ template [[nodiscard]] Type* try_cast() { if (node) { if (const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if (const auto* base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto* curr) { return curr->type()->info == info; }, node); base) { return static_cast(const_cast *>(base->cast(static_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 [[nodiscard]] Type cast() const { auto* const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc cast */ template [[nodiscard]] Type cast() { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /** * @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 [[nodiscard]] meta_any allow_cast() const { if (try_cast>() != nullptr) { return as_ref(); } else if (node) { if (const auto* const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto* curr) { return curr->type()->info == info; }, node); conv) { return conv->conv(storage.data()); } } return {}; } /** * @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 bool allow_cast() { // forces const on non-reference types to make them work also with wrappers for const references if (try_cast>() != nullptr) { return true; } else if (node) { if (const auto* const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto* curr) { return curr->type()->info == info; }, node); conv) { *this = conv->conv(std::as_const(storage).data()); return true; } } return false; } /** * @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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, storage, node); storage.emplace(std::forward(args)...); node = internal::meta_info::resolve(); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); 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; }; /** * @brief Checks if two wrappers differ in their content. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ [[nodiscard]] inline bool operator!=(const meta_any& lhs, const meta_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @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. * @return A properly initialized wrapper for an object of the given type. */ template meta_any make_meta(Args &&... args) { return meta_any{ std::in_place_type, std::forward(args)... }; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @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 meta_any forward_as_meta(Type&& value) { return meta_any{ std::in_place_type, std::decay_t, Type>>, std::forward(value) }; } /** * @brief Opaque pointers to instances of any type. * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
* Handles are used to generate references to actual objects when needed. */ struct meta_handle { /*! @brief Default constructor. */ meta_handle() = default; /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle&) = delete; /*! @brief Default move constructor. */ meta_handle(meta_handle&&) = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This meta handle. */ meta_handle& operator=(const meta_handle&) = delete; /** * @brief Default move assignment operator. * @return This meta handle. */ meta_handle& operator=(meta_handle&&) = default; /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(Type& value) ENTT_NOEXCEPT : meta_handle{} { if constexpr (std::is_same_v, meta_any>) { any = value.as_ref(); } else { any.emplace(value); } } /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(any); } /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any* operator->() { return &any; } /*! @copydoc operator-> */ [[nodiscard]] const meta_any* operator->() const { return &any; } private: meta_any any; }; /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Node type. */ using node_type = internal::meta_prop_node; /** * @brief Constructs an instance from a given node. * @param curr The underlying node with which to construct the instance. */ meta_prop(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Returns the stored key as a const reference. * @return A wrapper containing the key stored with the property. */ [[nodiscard]] meta_any key() const { return node->id.as_ref(); } /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for constructors. */ struct meta_ctor { /*! @brief Node type. */ using node_type = internal::meta_ctor_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_ctor(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Returns the type to which an object belongs. * @return The type to which the object belongs. */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a constructor. * @return The number of arguments accepted by the constructor. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Returns the type of the i-th argument of a constructor. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a constructor. */ [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any invoke(meta_any* const args, const size_type sz) const { return sz == arity() ? node->invoke(args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(arguments, sizeof...(Args)); } /** * @brief Returns a range to use to visit all properties. * @return An iterable range to use to visit all properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Node type. */ using node_type = internal::meta_data_node; /*! @copydoc meta_prop::meta_prop */ meta_data(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /*! @copydoc meta_any::type */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* 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 instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(meta_handle instance, Type&& value) const { return node->set && node->set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { return node->get(std::move(instance)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Node type. */ using node_type = internal::meta_func_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_func(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ [[nodiscard]] inline meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Invokes the underlying function, if possible. * * To invoke a member function, the parameters must be such that a cast or * conversion to the required types is possible. Otherwise, an empty and * thus invalid wrapper is returned.
* It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any* const args, const size_type sz) const { return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(std::move(instance), arguments, sizeof...(Args)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for types. */ class meta_type { static bool can_cast_or_convert(const internal::meta_type_node* type, const type_info info) ENTT_NOEXCEPT { if (type->info == info) { return true; } for (const auto* curr = type->conv; curr; curr = curr->next) { if (curr->type()->info == info) { return true; } } for (const auto* curr = type->base; curr; curr = curr->next) { if (auto* target = curr->type(); can_cast_or_convert(target, info)) { return true; } } return false; } template [[nodiscard]] static const internal::meta_ctor_node* ctor(const internal::meta_ctor_node* curr, std::index_sequence) { for (; curr; curr = curr->next) { if (curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { return curr; } } return nullptr; } template void unregister_all(Node** curr) { while (*curr) { (unregister_all(&((*curr)->*Member)), ...); *curr = std::exchange((*curr)->next, nullptr); } } public: /*! @brief Node type. */ using node_type = internal::meta_type_node; /*! @brief Node type. */ using base_node_type = internal::meta_base_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_type(node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Constructs an instance from a given base node. * @param curr The base node with which to construct the instance. */ meta_type(base_node_type* curr) ENTT_NOEXCEPT : node{ curr ? curr->type() : nullptr } {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ [[nodiscard]] type_info info() const ENTT_NOEXCEPT { return node->info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { return node->size_of; } /** * @brief Checks whether a type refers to void or not. * @return True if the underlying type is void, false otherwise. */ [[nodiscard]] bool is_void() const ENTT_NOEXCEPT { return node->is_void; } /** * @brief Checks whether a type refers to an integral type or not. * @return True if the underlying type is an integral type, false otherwise. */ [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT { return node->is_integral; } /** * @brief Checks whether a type refers to a floating-point type or not. * @return True if the underlying type is a floating-point type, false * otherwise. */ [[nodiscard]] bool is_floating_point() const ENTT_NOEXCEPT { return node->is_floating_point; } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { return node->is_array; } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { return node->is_enum; } /** * @brief Checks whether a type refers to an union or not. * @return True if the underlying type is an union, false otherwise. */ [[nodiscard]] bool is_union() const ENTT_NOEXCEPT { return node->is_union; } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { return node->is_class; } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { return node->is_pointer; } /** * @brief Checks whether a type refers to a function pointer or not. * @return True if the underlying type is a function pointer, false * otherwise. */ [[nodiscard]] bool is_function_pointer() const ENTT_NOEXCEPT { return node->is_function_pointer; } /** * @brief Checks whether a type refers to a pointer to data member or not. * @return True if the underlying type is a pointer to data member, false * otherwise. */ [[nodiscard]] bool is_member_object_pointer() const ENTT_NOEXCEPT { return node->is_member_object_pointer; } /** * @brief Checks whether a type refers to a pointer to member function or * not. * @return True if the underlying type is a pointer to member function, * false otherwise. */ [[nodiscard]] bool is_member_function_pointer() const ENTT_NOEXCEPT { return node->is_member_function_pointer; } /** * @brief Checks whether a type is a pointer-like type or not. * @return True if the underlying type is a pointer-like one, false * otherwise. */ [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { return node->is_pointer_like; } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { return node->is_sequence_container; } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { return node->is_associative_container; } /** * @brief Checks whether a type refers to a recognized class template * specialization or not. * @return True if the type is a recognized class template specialization, * false otherwise. */ [[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { return node->template_info.is_template_specialization; } /** * @brief Returns the number of template arguments, if any. * @return The number of template arguments, if any. */ [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { return node->template_info.arity; } /** * @brief Returns a tag for the class template of the underlying type. * * @sa meta_class_template_tag * * @return The tag for the class template of the underlying type. */ [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { return is_template_specialization() ? node->template_info.type() : meta_type{}; } /** * @brief Returns the type of the i-th template argument of a type. * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ [[nodiscard]] inline meta_type template_arg(size_type index) const ENTT_NOEXCEPT { return index < template_arity() ? node->template_info.arg(index) : meta_type{}; } /** * @brief Provides the number of dimensions of an array type. * @return The number of dimensions in case of array types, 0 otherwise. */ [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { return node->rank; } /** * @brief The number of elements along the given dimension of an array type. * @param dim The dimension of which to return the number of elements. * @return The number of elements along the given dimension in case of array * types, 0 otherwise. */ [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { return node->extent(dim); } /** * @brief Provides the type for which the pointer is defined. * @return The type for which the pointer is defined or this type if it * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { return node->remove_pointer(); } /** * @brief Provides the type for which the array is defined. * @return The type for which the array is defined or this type if it * doesn't refer to an array type. */ [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { return node->remove_extent(); } /** * @brief Returns a range to use to visit top-level base meta types. * @return An iterable range to use to visit top-level base meta types. */ [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { return node->base; } /** * @brief Returns the base meta type associated with a given identifier. * @param id Unique identifier. * @return The base meta type associated with the given identifier, if any. */ [[nodiscard]] meta_type base(const id_type id) const { return internal::meta_visit<&node_type::base>([id](const auto* curr) { return curr->type()->id == id; }, node); } /** * @brief Returns a range to use to visit top-level constructors. * @return An iterable range to use to visit top-level constructors. */ [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { return node->ctor; } /** * @brief Returns a constructor for a given list of types of arguments. * @tparam Args Constructor arguments. * @return The requested constructor, if any. */ template [[nodiscard]] meta_ctor ctor() const { return ctor(node->ctor, std::make_index_sequence{}); } /** * @brief Returns a range to use to visit top-level data. * @return An iterable range to use to visit top-level data. */ [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { return node->data; } /** * @brief Returns the data associated with a given identifier. * * The data of the base classes will also be visited, if any. * * @param id Unique identifier. * @return The data associated with the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { return internal::meta_visit<&node_type::data>([id](const auto* curr) { return curr->id == id; }, node); } /** * @brief Returns a range to use to visit top-level functions. * @return An iterable range to use to visit top-level functions. */ [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { return node->func; } /** * @brief Returns the function associated with a given identifier. * * The functions of the base classes will also be visited, if any.
* In the case of overloaded functions, the first one with the required * identifier will be returned. * * @param id Unique identifier. * @return The function associated with the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { return internal::meta_visit<&node_type::func>([id](const auto* curr) { return curr->id == id; }, node); } /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any* const args, const size_type sz) const { meta_any ret{}; internal::meta_visit<&node_type::ctor>([args, sz, &ret](const auto* curr) { return (curr->arity == sz) && (ret = curr->invoke(args)); }, node); return ret; } /** * @copybrief construct * * @sa construct * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return construct(arguments, sizeof...(Args)); } /** * @brief Invokes a function given an identifier, if possible. * * It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @sa meta_func::invoke * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any* const args, const size_type sz) const { const internal::meta_func_node* candidate{}; size_type extent{ sz + 1u }; bool ambiguous{}; for (auto* it = internal::meta_visit<&node_type::func>([id, sz](const auto* curr) { return curr->id == id && curr->arity == sz; }, node); it && it->id == id && it->arity == sz; it = it->next) { size_type direct{}; size_type ext{}; for (size_type next{}; next < sz && next == (direct + ext); ++next) { const auto type = args[next].type(); const auto req = it->arg(next).info(); type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); } if ((direct + ext) == sz) { if (ext < extent) { candidate = it; extent = ext; ambiguous = false; } else if (ext == extent) { ambiguous = true; } } } return (candidate && !ambiguous) ? candidate->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(id, std::move(instance), arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* 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 instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, meta_handle instance, Type&& value) const { const auto candidate = data(id); return candidate && candidate.set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); return candidate ? candidate.get(std::move(instance)) : meta_any{}; } /** * @brief Returns a range to use to visit top-level properties. * @return An iterable range to use to visit top-level properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * * Properties of the base classes will also be visited, if any. * * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } /** * @brief Checks if two objects refer to the same type. * @param other The object with which to compare. * @return True if the objects refer to the same type, false otherwise. */ [[nodiscard]] bool operator==(const meta_type& other) const ENTT_NOEXCEPT { return (!node && !other.node) || (node && other.node && node->info == other.node->info); } /** * @brief Resets a type and all its parts. * * This function resets a type and all its data members, member functions * and properties, as well as its constructors, destructors and conversion * functions if any.
* Base classes aren't reset but the link between the two types is removed. * * The type is also removed from the list of searchable types. */ void reset() ENTT_NOEXCEPT { for (auto** it = internal::meta_context::global(); *it; it = &(*it)->next) { if (*it == node) { *it = (*it)->next; break; } } unregister_all(&node->prop); unregister_all(&node->base); unregister_all(&node->conv); unregister_all<&internal::meta_ctor_node::prop>(&node->ctor); unregister_all<&internal::meta_data_node::prop>(&node->data); unregister_all<&internal::meta_func_node::prop>(&node->func); node->id = {}; node->ctor = node->def_ctor; node->dtor = nullptr; } private: node_type* node; }; /** * @brief Checks if two objects refer to the same type. * @param lhs An object, either valid or not. * @param rhs An object, either valid or not. * @return False if the objects refer to the same node, true otherwise. */ [[nodiscard]] inline bool operator!=(const meta_type& lhs, const meta_type& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } [[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { return node; } template meta_any meta_any::invoke(const id_type id, Args &&... args) const { return type().invoke(id, *this, std::forward(args)...); } template meta_any meta_any::invoke(const id_type id, Args &&... args) { return type().invoke(id, *this, std::forward(args)...); } template bool meta_any::set(const id_type id, Type&& value) { return type().set(id, *this, std::forward(value)); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) const { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) { return type().get(id, *this); } [[nodiscard]] inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } [[nodiscard]] inline meta_type meta_data::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { return node->type(); } [[nodiscard]] inline meta_type meta_func::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { return node->ret(); } [[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } /*! @brief Opaque iterator for sequence containers. */ class meta_sequence_container::meta_iterator { /*! @brief A sequence container can access the underlying iterator. */ friend class meta_sequence_container; enum class operation { INCR, DEREF }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable(const operation op, const any& from, void* to) { switch (op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: static_cast(to)->emplace::reference>(*any_cast(from)); break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = meta_any; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs a meta iterator from a given iterator. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(It iter) : vtable{ &basic_vtable }, handle{ std::move(iter) } {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator& operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), * this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator& other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { meta_any other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type* vtable{}; any handle{}; }; template struct meta_sequence_container::meta_sequence_container_proxy { using traits_type = meta_sequence_container_traits; [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any& container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool resize(any& container, size_type sz) { auto* const cont = any_cast(&container); return cont && traits_type::resize(*cont, sz); } [[nodiscard]] static bool clear(any& container) { auto* const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ traits_type::begin(*cont) }; } return iterator{ traits_type::cbegin(any_cast(container)) }; } [[nodiscard]] static iterator end(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ traits_type::end(*cont) }; } return iterator{ traits_type::cend(any_cast(container)) }; } [[nodiscard]] static std::pair insert(any& container, iterator it, meta_any& value) { if (auto* const cont = any_cast(&container); cont) { // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector if (value.allow_cast() || value.allow_cast()) { const auto* element = value.try_cast>(); auto ret = traits_type::insert(*cont, any_cast(it.handle), element ? *element : value.cast()); return { iterator{std::move(ret.first)}, ret.second }; } } return {}; } [[nodiscard]] static std::pair erase(any& container, iterator it) { if (auto* const cont = any_cast(&container); cont) { auto ret = traits_type::erase(*cont, any_cast(it.handle)); return { iterator{std::move(ret.first)}, ret.second }; } return {}; } [[nodiscard]] static meta_any get(any& container, size_type pos) { if (auto* const cont = any_cast(&container); cont) { return meta_any{ std::in_place_type, traits_type::get(*cont, pos) }; } return meta_any{ std::in_place_type, traits_type::cget(any_cast(container), pos) }; } }; /** * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ [[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /** * @brief Returns the size of a container. * @return The size of the container. */ [[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /** * @brief Resizes a container to contain a given number of elements. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::resize(size_type sz) { return resize_fn(storage, sz); } /** * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { return clear_fn(storage); } /** * @brief Returns an iterator to the first element of a container. * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { return begin_fn(storage); } /** * @brief Returns an iterator that is past the last element of a container. * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { return end_fn(storage); } /** * @brief Inserts an element at a specified location of a container. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A pair consisting of an iterator to the inserted element (in case of * success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { return insert_fn(storage, it, value); } /** * @brief Removes a given element from a container. * @param it Iterator to the element to remove. * @return A pair consisting of an iterator following the last removed element * (in case of success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::erase(iterator it) { return erase_fn(storage, it); } /** * @brief Returns a reference to the element at a given location of a container * (no bounds checking is performed). * @param pos The position of the element to return. * @return A reference to the requested element properly wrapped. */ [[nodiscard]] inline meta_any meta_sequence_container::operator[](size_type pos) { return get_fn(storage, pos); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } /*! @brief Opaque iterator for associative containers. */ class meta_associative_container::meta_iterator { enum operation { INCR, DEREF }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable(const operation op, const any& from, void* to) { switch (op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: const auto& it = any_cast(from); if constexpr (KeyOnly) { static_cast*>(to)->first.emplace(*it); } else { static_cast*>(to)->first.emplacefirst))>(it->first); static_cast*>(to)->second.emplacesecond))>(it->second); } break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = std::pair; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs an meta iterator from a given iterator. * @tparam KeyOnly True if the container is also key-only, false otherwise. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(std::integral_constant, It iter) : vtable{ &basic_vtable }, handle{ std::move(iter) } {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator& operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), * this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator& other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { reference other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type* vtable{}; any handle{}; }; template struct meta_associative_container::meta_associative_container_proxy { using traits_type = meta_associative_container_traits; [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { if constexpr (is_key_only_meta_associative_container_v) { return meta_type{}; } else { return internal::meta_info::resolve(); } } [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any& container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool clear(any& container) { auto* const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::begin(*cont) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cbegin(any_cast(container)) }; } [[nodiscard]] static iterator end(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::end(*cont) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cend(any_cast(container)) }; } [[nodiscard]] static bool insert(any& container, meta_any& key, meta_any& value) { if (auto* const cont = any_cast(&container); cont && key.allow_cast()) { if constexpr (is_key_only_meta_associative_container_v) { return traits_type::insert(*cont, key.cast()); } else { return value.allow_cast() && traits_type::insert(*cont, key.cast(), value.cast()); } } return false; } [[nodiscard]] static bool erase(any& container, meta_any& key) { if (auto* const cont = any_cast(&container); cont && key.allow_cast()) { return traits_type::erase(*cont, key.cast()); } return false; } [[nodiscard]] static iterator find(any& container, meta_any& key) { if (key.allow_cast()) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::find(*cont, key.cast()) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cfind(any_cast(container), key.cast()) }; } return {}; } }; /** * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ [[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { return key_only_container; } /** * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { return key_type_fn(); } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { return mapped_type_fn(); } /*! @copydoc meta_sequence_container::value_type */ [[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { return begin_fn(storage); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { return end_fn(storage); } /** * @brief Inserts an element (a key/value pair) into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { return insert_fn(storage, key, value); } /** * @brief Removes the specified element from a container. * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ inline bool meta_associative_container::erase(meta_any key) { return erase_fn(storage, key); } /** * @brief Returns an iterator to the element with a given key, if any. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { return find_fn(storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } } #endif // #include "node.hpp" // #include "policy.hpp" #ifndef ENTT_META_POLICY_HPP #define ENTT_META_POLICY_HPP namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ struct as_ref_t {}; /*! @brief Empty class type used to request the _as cref_ policy. */ struct as_cref_t {}; /*! @brief Empty class type used to request the _as-is_ policy. */ struct as_is_t {}; /*! @brief Empty class type used to request the _as void_ policy. */ struct as_void_t {}; } #endif // #include "utility.hpp" #ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "policy.hpp" namespace entt { /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = std::conditional_t, type_list, type_list>; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = true; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = !std::is_same_v; }; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = std::conditional_t, type_list, type_list>; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = false; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = !std::is_same_v; }; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = type_list; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = false; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = true; }; /** * @brief Meta function helper. * * Converts a function type to be associated with a reflected type into its meta * function descriptor. * * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template class meta_function_helper { template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class::*)(Args...) const); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class::*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(*)(Args...)); public: /*! @brief The meta function descriptor of the given function. */ using type = decltype(get_rid_of_noexcept(std::declval())); }; /** * @brief Helper type. * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template using meta_function_helper_t = typename meta_function_helper::type; /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Args Actual types of arguments. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { return internal::meta_arg_node(type_list{}, index); } /** * @brief Constructs an instance given a list of erased parameters, if possible. * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @tparam Index Indexes to use to extract erased arguments from their list. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(meta_any* const args, std::index_sequence) { if (((args + Index)->allow_cast() && ...)) { return Type{ (args + Index)->cast()... }; } return {}; } /** * @brief Sets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to set. * @param instance An opaque instance of the underlying type, if required. * @param value Parameter to use to set the variable. * @return True in case of success, false otherwise. */ template [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { if constexpr (!std::is_same_v && !std::is_same_v) { if constexpr (std::is_function_v>>) { using data_type = type_list_element_t<1u, typename meta_function_helper_t::args_type>; if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { Data(*clazz, value.cast()); return true; } } else if constexpr (std::is_member_function_pointer_v) { using data_type = type_list_element_t<0u, typename meta_function_helper_t::args_type>; if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { (clazz->*Data)(value.cast()); return true; } } else if constexpr (std::is_member_object_pointer_v) { using data_type = std::remove_reference_t().*Data)>; if constexpr (!std::is_array_v && !std::is_const_v) { if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { clazz->*Data = value.cast(); return true; } } } else { using data_type = std::remove_reference_t; if constexpr (!std::is_array_v && !std::is_const_v) { if (value.allow_cast()) { *Data = value.cast(); return true; } } } } return false; } /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param value Value to wrap. * @return A meta any containing the returned value. */ template meta_any meta_dispatch(Type&& value) { if constexpr (std::is_same_v) { return meta_any{ std::in_place_type, std::forward(value) }; } else if constexpr (std::is_same_v) { return meta_any{ std::in_place_type, std::forward(value) }; } else if constexpr (std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ std::in_place_type&>, std::as_const(value) }; } else { static_assert(std::is_same_v, "Policy not supported"); return meta_any{ std::forward(value) }; } } /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { if constexpr (std::is_function_v>>) { auto* const clazz = instance->try_cast, const Type, Type>>(); return clazz ? meta_dispatch(Data(*clazz)) : meta_any{}; } else if constexpr (std::is_member_function_pointer_v) { auto* const clazz = instance->try_cast, const Type, Type>>(); return clazz ? meta_dispatch((clazz->*Data)()) : meta_any{}; } else if constexpr (std::is_member_object_pointer_v) { if constexpr (!std::is_array_v().*Data)>>>) { if (auto* clazz = instance->try_cast(); clazz) { return meta_dispatch(clazz->*Data); } else if (auto* fallback = instance->try_cast(); fallback) { return meta_dispatch(fallback->*Data); } } return meta_any{}; } else if constexpr (std::is_pointer_v) { if constexpr (std::is_array_v>) { return meta_any{}; } else { return meta_dispatch(*Data); } } else { return meta_dispatch(Data); } } /** * @brief Invokes a function given a list of erased parameters, if possible. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @tparam Index Indexes to use to extract erased arguments from their list. * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke([[maybe_unused]] meta_handle instance, meta_any* args, std::index_sequence) { using descriptor = meta_function_helper_t; const auto invoke = [](auto&& maybe_clazz, auto &&... other) { if constexpr (std::is_member_function_pointer_v) { if constexpr (std::is_void_v) { (std::forward(maybe_clazz).*Candidate)(std::forward(other)...); return meta_any{ std::in_place_type }; } else { return meta_dispatch((std::forward(maybe_clazz).*Candidate)(std::forward(other)...)); } } else { if constexpr (std::is_void_v) { Candidate(std::forward(maybe_clazz), std::forward(other)...); return meta_any{ std::in_place_type }; } else { return meta_dispatch(Candidate(std::forward(maybe_clazz), std::forward(other)...)); } } }; if constexpr (std::is_invocable_v...>) { if (const auto* const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return invoke(*clazz, (args + Index)->cast>()...); } } else if constexpr (std::is_invocable_v...>) { if (auto* const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return invoke(*clazz, (args + Index)->cast>()...); } } else { if (((args + Index)->allow_cast>() && ...)) { return invoke((args + Index)->cast>()...); } } return meta_any{}; } /** * @brief Invokes a function given a list of erased parameters, if possible. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @tparam Index Indexes to use to extract erased arguments from their list. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle, meta_any*, std::index_sequence) { if constexpr (std::is_void_v) { Candidate(); return meta_any{ std::in_place_type }; } else { return meta_dispatch(Candidate()); } } } #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] bool find_if(const Node* candidate, const Node* node) ENTT_NOEXCEPT { return node && (node == candidate || find_if(candidate, node->next)); } template [[nodiscard]] bool find_if_not(const Id id, Node* node, const Node* owner) ENTT_NOEXCEPT { if constexpr (std::is_pointer_v) { return node && ((*node->id == *id && node != owner) || find_if_not(id, node->next, owner)); } else { return node && ((node->id == id && node != owner) || find_if_not(id, node->next, owner)); } } } /** * Internal details not to be documented. * @endcond */ /** * @brief Meta factory to be used for reflection purposes. * * The meta factory is an utility class used to reflect types, data members and * functions of all sorts. This class ensures that the underlying web of types * is built correctly and performs some checks in debug mode to ensure that * there are no subtle errors at runtime. */ template struct meta_factory; /** * @brief Extended meta factory to be used for reflection purposes. * @tparam Type Reflected type for which the factory was created. * @tparam Spec Property specialization pack used to disambiguate overloads. */ template struct meta_factory : public meta_factory { private: template void unpack(std::index_sequence, std::tuple property, Other &&... other) { unroll(choice<3>, std::move(std::get(property))..., std::forward(other)...); } template void unroll(choice_t<3>, std::tuple property, Other &&... other) { unpack(std::index_sequence_for{}, std::move(property), std::forward(other)...); } template void unroll(choice_t<2>, std::pair property, Other &&... other) { assign(std::move(property.first), std::move(property.second)); unroll(choice<3>, std::forward(other)...); } template std::enable_if_t> unroll(choice_t<1>, Property&& property, Other &&... other) { assign(std::forward(property)); unroll(choice<3>, std::forward(other)...); } template void unroll(choice_t<0>, Func&& invocable, Other &&... other) { unroll(choice<3>, std::forward(invocable)(), std::forward(other)...); } template void unroll(choice_t<0>) {} template void assign(Key&& key, meta_any value = {}) { static meta_any property[2u]{}; static internal::meta_prop_node node{ nullptr, property[0u], property[1u] }; entt::meta_any instance{ std::forward(key) }; ENTT_ASSERT(!internal::find_if_not(&instance, *curr, &node), "Duplicate key"); property[0u] = std::move(instance); property[1u] = std::move(value); if (!internal::find_if(&node, *curr)) { node.next = *curr; *curr = &node; } } public: /** * @brief Constructs an extended factory from a given node. * @param target The underlying node to which to assign the properties. */ meta_factory(internal::meta_prop_node** target) ENTT_NOEXCEPT : curr{ target } {} /** * @brief Assigns a property to the last meta object created. * * Both the key and the value (if any) must be at least copy constructible. * * @tparam PropertyOrKey Type of the property or property key. * @tparam Value Optional type of the property value. * @param property_or_key Property or property key. * @param value Optional property value. * @return A meta factory for the parent type. */ template auto prop(PropertyOrKey&& property_or_key, Value &&... value)&& { if constexpr (sizeof...(Value) == 0) { unroll(choice<3>, std::forward(property_or_key)); } else { assign(std::forward(property_or_key), std::forward(value)...); } return meta_factory{curr}; } /** * @brief Assigns properties to the last meta object created. * * Both the keys and the values (if any) must be at least copy * constructible. * * @tparam Property Types of the properties. * @param property Properties to assign to the last meta object created. * @return A meta factory for the parent type. */ template auto props(Property... property)&& { unroll(choice<3>, std::forward(property)...); return meta_factory{curr}; } private: internal::meta_prop_node** curr; }; /** * @brief Basic meta factory to be used for reflection purposes. * @tparam Type Reflected type for which the factory was created. */ template struct meta_factory { /** * @brief Makes a meta type _searchable_. * @param id Optional unique identifier. * @return An extended meta factory for the given type. */ auto type(const id_type id = type_hash::value()) { auto* const node = internal::meta_info::resolve(); ENTT_ASSERT(!internal::find_if_not(id, *internal::meta_context::global(), node), "Duplicate identifier"); node->id = id; if (!internal::find_if(node, *internal::meta_context::global())) { node->next = *internal::meta_context::global(); *internal::meta_context::global() = node; } return meta_factory{&node->prop}; } /** * @brief Assigns a meta base to a meta type. * * A reflected base class must be a real base class of the reflected type. * * @tparam Base Type of the base class to assign to the meta type. * @return A meta factory for the parent type. */ template auto base() ENTT_NOEXCEPT { static_assert(std::is_base_of_v, "Invalid base type"); auto* const type = internal::meta_info::resolve(); static internal::meta_base_node node{ type, nullptr, &internal::meta_info::resolve, [](const void* instance) ENTT_NOEXCEPT -> const void* { return static_cast(static_cast(instance)); } }; if (!internal::find_if(&node, type->base)) { node.next = type->base; type->base = &node; } return meta_factory{}; } /** * @brief Assigns a meta conversion function to a meta type. * * Conversion functions can be either free functions or member * functions.
* In case of free functions, they must accept a const reference to an * instance of the parent type as an argument. In case of member functions, * they should have no arguments at all. * * @tparam Candidate The actual function to use for the conversion. * @return A meta factory for the parent type. */ template std::enable_if_t, meta_factory> conv() ENTT_NOEXCEPT { using conv_type = std::invoke_result_t; auto* const type = internal::meta_info::resolve(); static internal::meta_conv_node node{ type, nullptr, &internal::meta_info::resolve, [](const void* instance) -> meta_any { return (static_cast(instance)->*Candidate)(); } }; if (!internal::find_if(&node, type->conv)) { node.next = type->conv; type->conv = &node; } return meta_factory{}; } /*! @copydoc conv */ template std::enable_if_t, meta_factory> conv() ENTT_NOEXCEPT { using conv_type = std::invoke_result_t; auto* const type = internal::meta_info::resolve(); static internal::meta_conv_node node{ type, nullptr, &internal::meta_info::resolve, [](const void* instance) -> meta_any { return Candidate(*static_cast(instance)); } }; if (!internal::find_if(&node, type->conv)) { node.next = type->conv; type->conv = &node; } return meta_factory{}; } /** * @brief Assigns a meta conversion function to a meta type. * * The given type must be such that an instance of the reflected type can be * converted to it. * * @tparam To Type of the conversion function to assign to the meta type. * @return A meta factory for the parent type. */ template auto conv() ENTT_NOEXCEPT { static_assert(std::is_convertible_v, "Could not convert to the required type"); auto* const type = internal::meta_info::resolve(); static internal::meta_conv_node node{ type, nullptr, &internal::meta_info::resolve, [](const void* instance) -> meta_any { return static_cast(*static_cast(instance)); } }; if (!internal::find_if(&node, type->conv)) { node.next = type->conv; type->conv = &node; } return meta_factory{}; } /** * @brief Assigns a meta constructor to a meta type. * * Both member functions and free function can be assigned to meta types in * the role of constructors. All that is required is that they return an * instance of the underlying type.
* From a client's point of view, nothing changes if a constructor of a meta * type is a built-in one or not. * * @tparam Candidate The actual function to use as a constructor. * @tparam Policy Optional policy (no policy set by default). * @return An extended meta factory for the parent type. */ template auto ctor() ENTT_NOEXCEPT { using descriptor = meta_function_helper_t; static_assert(std::is_same_v, Type>, "The function doesn't return an object of the required type"); auto* const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, descriptor::args_type::size, [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { return meta_arg(typename descriptor::args_type{}, index); }, [](meta_any* const args) { return meta_invoke({}, args, std::make_index_sequence{}); } }; if (!internal::find_if(&node, type->ctor)) { node.next = type->ctor; type->ctor = &node; } return meta_factory>{&node.prop}; } /** * @brief Assigns a meta constructor to a meta type. * * A meta constructor is uniquely identified by the types of its arguments * and is such that there exists an actual constructor of the underlying * type that can be invoked with parameters whose types are those given. * * @tparam Args Types of arguments to use to construct an instance. * @return An extended meta factory for the parent type. */ template auto ctor() ENTT_NOEXCEPT { using descriptor = meta_function_helper_t; auto* const type = internal::meta_info::resolve(); static internal::meta_ctor_node node{ type, nullptr, nullptr, descriptor::args_type::size, [](const typename internal::meta_ctor_node::size_type index) ENTT_NOEXCEPT { return meta_arg(typename descriptor::args_type{}, index); }, [](meta_any* const args) { return meta_construct(args, std::make_index_sequence{}); } }; if (!internal::find_if(&node, type->ctor)) { node.next = type->ctor; type->ctor = &node; } return meta_factory{&node.prop}; } /** * @brief Assigns a meta destructor to a meta type. * * Free functions can be assigned to meta types in the role of destructors. * The signature of the function should identical to the following: * * @code{.cpp} * void(Type &); * @endcode * * The purpose is to give users the ability to free up resources that * require special treatment before an object is actually destroyed. * * @tparam Func The actual function to use as a destructor. * @return A meta factory for the parent type. */ template auto dtor() ENTT_NOEXCEPT { static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); auto* const type = internal::meta_info::resolve(); type->dtor = [](void* instance) { Func(*static_cast(instance)); }; return meta_factory{}; } /** * @brief Assigns a meta data to a meta type. * * Both data members and static and global variables, as well as constants * of any kind, can be assigned to a meta type.
* From a client's point of view, all the variables associated with the * reflected object will appear as if they were part of the type itself. * * @tparam Data The actual variable to attach to the meta type. * @tparam Policy Optional policy (no policy set by default). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto data(const id_type id) ENTT_NOEXCEPT { if constexpr (std::is_member_object_pointer_v) { return data(id); } else { using data_type = std::remove_pointer_t; auto* const type = internal::meta_info::resolve(); static internal::meta_data_node node{ {}, type, nullptr, nullptr, std::is_same_v || std::is_const_v, true, &internal::meta_info::resolve, &meta_setter, &meta_getter }; ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier"); node.id = id; if (!internal::find_if(&node, type->data)) { node.next = type->data; type->data = &node; } return meta_factory>{&node.prop}; } } /** * @brief Assigns a meta data to a meta type by means of its setter and * getter. * * Setters and getters can be either free functions, member functions or a * mix of them.
* In case of free functions, setters and getters must accept a reference to * an instance of the parent type as their first argument. A setter has then * an extra argument of a type convertible to that of the parameter to * set.
* In case of member functions, getters have no arguments at all, while * setters has an argument of a type convertible to that of the parameter to * set. * * @tparam Setter The actual function to use as a setter. * @tparam Getter The actual function to use as a getter. * @tparam Policy Optional policy (no policy set by default). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto data(const id_type id) ENTT_NOEXCEPT { using underlying_type = std::remove_reference_t>; auto* const type = internal::meta_info::resolve(); static internal::meta_data_node node{ {}, type, nullptr, nullptr, std::is_same_v || (std::is_member_object_pointer_v && std::is_const_v), false, &internal::meta_info::resolve, &meta_setter, &meta_getter }; ENTT_ASSERT(!internal::find_if_not(id, type->data, &node), "Duplicate identifier"); node.id = id; if (!internal::find_if(&node, type->data)) { node.next = type->data; type->data = &node; } return meta_factory, std::integral_constant>{&node.prop}; } /** * @brief Assigns a meta funcion to a meta type. * * Both member functions and free functions can be assigned to a meta * type.
* From a client's point of view, all the functions associated with the * reflected object will appear as if they were part of the type itself. * * @tparam Candidate The actual function to attach to the meta type. * @tparam Policy Optional policy (no policy set by default). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto func(const id_type id) ENTT_NOEXCEPT { using descriptor = meta_function_helper_t; auto* const type = internal::meta_info::resolve(); static internal::meta_func_node node{ {}, type, nullptr, nullptr, descriptor::args_type::size, descriptor::is_const, descriptor::is_static, &internal::meta_info, void, typename descriptor::return_type>>::resolve, [](const typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { return meta_arg(typename descriptor::args_type{}, index); }, [](meta_handle instance, meta_any* args) { return meta_invoke(std::move(instance), args, std::make_index_sequence{}); } }; for (auto* it = &type->func; *it; it = &(*it)->next) { if (*it == &node) { *it = node.next; break; } } internal::meta_func_node** it = &type->func; for (; *it && (*it)->id != id; it = &(*it)->next); for (; *it && (*it)->id == id && (*it)->arity < node.arity; it = &(*it)->next); node.id = id; node.next = *it; *it = &node; return meta_factory>{&node.prop}; } }; /** * @brief Utility function to use for reflection. * * This is the point from which everything starts.
* By invoking this function with a type that is not yet reflected, a meta type * is created to which it will be possible to attach meta objects through a * dedicated factory. * * @tparam Type Type to reflect. * @return A meta factory for the given type. */ template [[nodiscard]] auto meta() ENTT_NOEXCEPT { auto* const node = internal::meta_info::resolve(); // extended meta factory to allow assigning properties to opaque meta types return meta_factory{&node->prop}; } } #endif // #include "meta/meta.hpp" #ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/any.hpp" // #include "../core/fwd.hpp" // #include "../core/utility.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "adl_pointer.hpp" // #include "ctx.hpp" // #include "node.hpp" // #include "range.hpp" // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; /*! @brief Proxy object for sequence containers. */ class meta_sequence_container { template struct meta_sequence_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_sequence_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for sequence containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : value_type_fn{ &meta_sequence_container_proxy::value_type }, size_fn{ &meta_sequence_container_proxy::size }, resize_fn{ &meta_sequence_container_proxy::resize }, clear_fn{ &meta_sequence_container_proxy::clear }, begin_fn{ &meta_sequence_container_proxy::begin }, end_fn{ &meta_sequence_container_proxy::end }, insert_fn{ &meta_sequence_container_proxy::insert }, erase_fn{ &meta_sequence_container_proxy::erase }, get_fn{ &meta_sequence_container_proxy::get }, storage{ std::move(instance) } {} [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool resize(size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline std::pair insert(iterator, meta_any); inline std::pair erase(iterator); [[nodiscard]] inline meta_any operator[](size_type); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: meta_type(*value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(*size_fn)(const any&) ENTT_NOEXCEPT = nullptr; bool(*resize_fn)(any&, size_type) = nullptr; bool(*clear_fn)(any&) = nullptr; iterator(*begin_fn)(any&) = nullptr; iterator(*end_fn)(any&) = nullptr; std::pair(*insert_fn)(any&, iterator, meta_any&) = nullptr; std::pair(*erase_fn)(any&, iterator) = nullptr; meta_any(*get_fn)(any&, size_type) = nullptr; any storage{}; }; /*! @brief Proxy object for associative containers. */ class meta_associative_container { template struct meta_associative_container_proxy; class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /*! @brief Default constructor. */ meta_associative_container() ENTT_NOEXCEPT = default; /** * @brief Construct a proxy object for associative containers. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT : key_only_container{ is_key_only_meta_associative_container_v }, key_type_fn{ &meta_associative_container_proxy::key_type }, mapped_type_fn{ &meta_associative_container_proxy::mapped_type }, value_type_fn{ &meta_associative_container_proxy::value_type }, size_fn{ &meta_associative_container_proxy::size }, clear_fn{ &meta_associative_container_proxy::clear }, begin_fn{ &meta_associative_container_proxy::begin }, end_fn{ &meta_associative_container_proxy::end }, insert_fn{ &meta_associative_container_proxy::insert }, erase_fn{ &meta_associative_container_proxy::erase }, find_fn{ &meta_associative_container_proxy::find }, storage{ std::move(instance) } {} [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline bool insert(meta_any, meta_any); inline bool erase(meta_any); [[nodiscard]] inline iterator find(meta_any); [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; private: bool key_only_container{}; meta_type(*key_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(*mapped_type_fn)() ENTT_NOEXCEPT = nullptr; meta_type(*value_type_fn)() ENTT_NOEXCEPT = nullptr; size_type(*size_fn)(const any&) ENTT_NOEXCEPT = nullptr; bool(*clear_fn)(any&) = nullptr; iterator(*begin_fn)(any&) = nullptr; iterator(*end_fn)(any&) = nullptr; bool(*insert_fn)(any&, meta_any&, meta_any&) = nullptr; bool(*erase_fn)(any&, meta_any&) = nullptr; iterator(*find_fn)(any&, meta_any&) = nullptr; any storage{}; }; /*! @brief Opaque wrapper for values of any type. */ class meta_any { enum class operation { DTOR, DEREF, SEQ, ASSOC }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any& from, [[maybe_unused]] void* to) { static_assert(std::is_same_v>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { switch (op) { case operation::DTOR: if (auto* curr = static_cast(to); curr->dtor && from.owner()) { curr->dtor(const_cast(from).data()); } break; case operation::DEREF: if constexpr (is_meta_pointer_like_v) { using element_type = std::remove_const_t::element_type>; if constexpr (std::is_function_v) { *static_cast(to) = any_cast(from); } else if constexpr (!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(from))); static_cast(to)->emplace(adl_meta_pointer_like::dereference(any_cast(from))); } } case operation::SEQ: if constexpr (is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; case operation::ASSOC: if constexpr (is_complete_v>) { *static_cast(to) = { std::in_place_type, std::move(const_cast(from)) }; } break; } } } 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 } {} public: /*! @brief Default constructor. */ meta_any() ENTT_NOEXCEPT : storage{}, node{}, vtable{ &basic_vtable } {} /** * @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 explicit meta_any(std::in_place_type_t, Args &&... args) : storage{ std::in_place_type, std::forward(args)... }, node{ internal::meta_info::resolve() }, vtable{ &basic_vtable>> } {} /** * @brief Constructs a wrapper 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 meta_any(std::reference_wrapper value) : meta_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, meta_any>>> meta_any(Type&& value) : storage{ std::forward(value) }, node{ internal::meta_info>::resolve() }, vtable{ &basic_vtable> } {} /** * @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) } {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { vtable(operation::DTOR, storage, node); } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This meta any object. */ meta_any& operator=(const meta_any& other) { std::exchange(vtable, other.vtable)(operation::DTOR, storage, node); 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 { std::exchange(vtable, std::exchange(other.vtable, &basic_vtable))(operation::DTOR, storage, node); 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 [[deprecated("Use std::in_place_type, entt::make_meta, emplace or forward_as_meta instead")]] meta_any& operator=(std::reference_wrapper value) { emplace(value.get()); 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 std::enable_if_t, meta_any>, meta_any&> operator=(Type&& value) { emplace>(std::forward(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 meta_any invoke(const id_type id, Args &&... args) const; /*! @copydoc invoke */ template 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 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 [[nodiscard]] const Type* try_cast() const { if (node) { if (const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if (const auto* base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto* curr) { return curr->type()->info == info; }, node); base) { return static_cast(base->cast(storage.data())); } } return nullptr; } /*! @copydoc try_cast */ template [[nodiscard]] Type* try_cast() { if (node) { if (const auto info = type_id(); node->info == info) { return any_cast(&storage); } else if (const auto* base = internal::meta_visit<&internal::meta_type_node::base>([info](const auto* curr) { return curr->type()->info == info; }, node); base) { return static_cast(const_cast *>(base->cast(static_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 [[nodiscard]] Type cast() const { auto* const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc cast */ template [[nodiscard]] Type cast() { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /** * @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 [[nodiscard]] meta_any allow_cast() const { if (try_cast>() != nullptr) { return as_ref(); } else if (node) { if (const auto* const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto* curr) { return curr->type()->info == info; }, node); conv) { return conv->conv(storage.data()); } } return {}; } /** * @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 bool allow_cast() { // forces const on non-reference types to make them work also with wrappers for const references if (try_cast>() != nullptr) { return true; } else if (node) { if (const auto* const conv = internal::meta_visit<&internal::meta_type_node::conv>([info = type_id()](const auto* curr) { return curr->type()->info == info; }, node); conv) { *this = conv->conv(std::as_const(storage).data()); return true; } } return false; } /** * @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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, storage, node); storage.emplace(std::forward(args)...); node = internal::meta_info::resolve(); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(operation::DTOR, storage, node); 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; }; /** * @brief Checks if two wrappers differ in their content. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ [[nodiscard]] inline bool operator!=(const meta_any& lhs, const meta_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @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. * @return A properly initialized wrapper for an object of the given type. */ template meta_any make_meta(Args &&... args) { return meta_any{ std::in_place_type, std::forward(args)... }; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @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 meta_any forward_as_meta(Type&& value) { return meta_any{ std::in_place_type, std::decay_t, Type>>, std::forward(value) }; } /** * @brief Opaque pointers to instances of any type. * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
* Handles are used to generate references to actual objects when needed. */ struct meta_handle { /*! @brief Default constructor. */ meta_handle() = default; /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle&) = delete; /*! @brief Default move constructor. */ meta_handle(meta_handle&&) = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This meta handle. */ meta_handle& operator=(const meta_handle&) = delete; /** * @brief Default move assignment operator. * @return This meta handle. */ meta_handle& operator=(meta_handle&&) = default; /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(Type& value) ENTT_NOEXCEPT : meta_handle{} { if constexpr (std::is_same_v, meta_any>) { any = value.as_ref(); } else { any.emplace(value); } } /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(any); } /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any* operator->() { return &any; } /*! @copydoc operator-> */ [[nodiscard]] const meta_any* operator->() const { return &any; } private: meta_any any; }; /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Node type. */ using node_type = internal::meta_prop_node; /** * @brief Constructs an instance from a given node. * @param curr The underlying node with which to construct the instance. */ meta_prop(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Returns the stored key as a const reference. * @return A wrapper containing the key stored with the property. */ [[nodiscard]] meta_any key() const { return node->id.as_ref(); } /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for constructors. */ struct meta_ctor { /*! @brief Node type. */ using node_type = internal::meta_ctor_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_ctor(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Returns the type to which an object belongs. * @return The type to which the object belongs. */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a constructor. * @return The number of arguments accepted by the constructor. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Returns the type of the i-th argument of a constructor. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a constructor. */ [[nodiscard]] meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any invoke(meta_any* const args, const size_type sz) const { return sz == arity() ? node->invoke(args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any invoke([[maybe_unused]] Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(arguments, sizeof...(Args)); } /** * @brief Returns a range to use to visit all properties. * @return An iterable range to use to visit all properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Node type. */ using node_type = internal::meta_data_node; /*! @copydoc meta_prop::meta_prop */ meta_data(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /*! @copydoc meta_any::type */ [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* 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 instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(meta_handle instance, Type&& value) const { return node->set && node->set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { return node->get(std::move(instance)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Node type. */ using node_type = internal::meta_func_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_func(const node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /*! @copydoc meta_type::id */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /*! @copydoc meta_ctor::parent */ [[nodiscard]] inline meta_type parent() const ENTT_NOEXCEPT; /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { return node->arity; } /** * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { return node->is_const; } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ [[nodiscard]] bool is_static() const ENTT_NOEXCEPT { return node->is_static; } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ [[nodiscard]] inline meta_type ret() const ENTT_NOEXCEPT; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ [[nodiscard]] inline meta_type arg(size_type index) const ENTT_NOEXCEPT; /** * @brief Invokes the underlying function, if possible. * * To invoke a member function, the parameters must be such that a cast or * conversion to the required types is possible. Otherwise, an empty and * thus invalid wrapper is returned.
* It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any* const args, const size_type sz) const { return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(std::move(instance), arguments, sizeof...(Args)); } /*! @copydoc meta_ctor::prop */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } private: const node_type* node; }; /*! @brief Opaque wrapper for types. */ class meta_type { static bool can_cast_or_convert(const internal::meta_type_node* type, const type_info info) ENTT_NOEXCEPT { if (type->info == info) { return true; } for (const auto* curr = type->conv; curr; curr = curr->next) { if (curr->type()->info == info) { return true; } } for (const auto* curr = type->base; curr; curr = curr->next) { if (auto* target = curr->type(); can_cast_or_convert(target, info)) { return true; } } return false; } template [[nodiscard]] static const internal::meta_ctor_node* ctor(const internal::meta_ctor_node* curr, std::index_sequence) { for (; curr; curr = curr->next) { if (curr->arity == sizeof...(Args) && (can_cast_or_convert(internal::meta_info::resolve(), curr->arg(Index).info()) && ...)) { return curr; } } return nullptr; } template void unregister_all(Node** curr) { while (*curr) { (unregister_all(&((*curr)->*Member)), ...); *curr = std::exchange((*curr)->next, nullptr); } } public: /*! @brief Node type. */ using node_type = internal::meta_type_node; /*! @brief Node type. */ using base_node_type = internal::meta_base_node; /*! @brief Unsigned integer type. */ using size_type = typename node_type::size_type; /*! @copydoc meta_prop::meta_prop */ meta_type(node_type* curr = nullptr) ENTT_NOEXCEPT : node{ curr } {} /** * @brief Constructs an instance from a given base node. * @param curr The base node with which to construct the instance. */ meta_type(base_node_type* curr) ENTT_NOEXCEPT : node{ curr ? curr->type() : nullptr } {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ [[nodiscard]] type_info info() const ENTT_NOEXCEPT { return node->info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ [[nodiscard]] id_type id() const ENTT_NOEXCEPT { return node->id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { return node->size_of; } /** * @brief Checks whether a type refers to void or not. * @return True if the underlying type is void, false otherwise. */ [[nodiscard]] bool is_void() const ENTT_NOEXCEPT { return node->is_void; } /** * @brief Checks whether a type refers to an integral type or not. * @return True if the underlying type is an integral type, false otherwise. */ [[nodiscard]] bool is_integral() const ENTT_NOEXCEPT { return node->is_integral; } /** * @brief Checks whether a type refers to a floating-point type or not. * @return True if the underlying type is a floating-point type, false * otherwise. */ [[nodiscard]] bool is_floating_point() const ENTT_NOEXCEPT { return node->is_floating_point; } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { return node->is_array; } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ [[nodiscard]] bool is_enum() const ENTT_NOEXCEPT { return node->is_enum; } /** * @brief Checks whether a type refers to an union or not. * @return True if the underlying type is an union, false otherwise. */ [[nodiscard]] bool is_union() const ENTT_NOEXCEPT { return node->is_union; } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ [[nodiscard]] bool is_class() const ENTT_NOEXCEPT { return node->is_class; } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ [[nodiscard]] bool is_pointer() const ENTT_NOEXCEPT { return node->is_pointer; } /** * @brief Checks whether a type refers to a function pointer or not. * @return True if the underlying type is a function pointer, false * otherwise. */ [[nodiscard]] bool is_function_pointer() const ENTT_NOEXCEPT { return node->is_function_pointer; } /** * @brief Checks whether a type refers to a pointer to data member or not. * @return True if the underlying type is a pointer to data member, false * otherwise. */ [[nodiscard]] bool is_member_object_pointer() const ENTT_NOEXCEPT { return node->is_member_object_pointer; } /** * @brief Checks whether a type refers to a pointer to member function or * not. * @return True if the underlying type is a pointer to member function, * false otherwise. */ [[nodiscard]] bool is_member_function_pointer() const ENTT_NOEXCEPT { return node->is_member_function_pointer; } /** * @brief Checks whether a type is a pointer-like type or not. * @return True if the underlying type is a pointer-like one, false * otherwise. */ [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { return node->is_pointer_like; } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const ENTT_NOEXCEPT { return node->is_sequence_container; } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const ENTT_NOEXCEPT { return node->is_associative_container; } /** * @brief Checks whether a type refers to a recognized class template * specialization or not. * @return True if the type is a recognized class template specialization, * false otherwise. */ [[nodiscard]] bool is_template_specialization() const ENTT_NOEXCEPT { return node->template_info.is_template_specialization; } /** * @brief Returns the number of template arguments, if any. * @return The number of template arguments, if any. */ [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { return node->template_info.arity; } /** * @brief Returns a tag for the class template of the underlying type. * * @sa meta_class_template_tag * * @return The tag for the class template of the underlying type. */ [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { return is_template_specialization() ? node->template_info.type() : meta_type{}; } /** * @brief Returns the type of the i-th template argument of a type. * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ [[nodiscard]] inline meta_type template_arg(size_type index) const ENTT_NOEXCEPT { return index < template_arity() ? node->template_info.arg(index) : meta_type{}; } /** * @brief Provides the number of dimensions of an array type. * @return The number of dimensions in case of array types, 0 otherwise. */ [[nodiscard]] size_type rank() const ENTT_NOEXCEPT { return node->rank; } /** * @brief The number of elements along the given dimension of an array type. * @param dim The dimension of which to return the number of elements. * @return The number of elements along the given dimension in case of array * types, 0 otherwise. */ [[nodiscard]] size_type extent(size_type dim = {}) const ENTT_NOEXCEPT { return node->extent(dim); } /** * @brief Provides the type for which the pointer is defined. * @return The type for which the pointer is defined or this type if it * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const ENTT_NOEXCEPT { return node->remove_pointer(); } /** * @brief Provides the type for which the array is defined. * @return The type for which the array is defined or this type if it * doesn't refer to an array type. */ [[nodiscard]] meta_type remove_extent() const ENTT_NOEXCEPT { return node->remove_extent(); } /** * @brief Returns a range to use to visit top-level base meta types. * @return An iterable range to use to visit top-level base meta types. */ [[nodiscard]] meta_range base() const ENTT_NOEXCEPT { return node->base; } /** * @brief Returns the base meta type associated with a given identifier. * @param id Unique identifier. * @return The base meta type associated with the given identifier, if any. */ [[nodiscard]] meta_type base(const id_type id) const { return internal::meta_visit<&node_type::base>([id](const auto* curr) { return curr->type()->id == id; }, node); } /** * @brief Returns a range to use to visit top-level constructors. * @return An iterable range to use to visit top-level constructors. */ [[nodiscard]] meta_range ctor() const ENTT_NOEXCEPT { return node->ctor; } /** * @brief Returns a constructor for a given list of types of arguments. * @tparam Args Constructor arguments. * @return The requested constructor, if any. */ template [[nodiscard]] meta_ctor ctor() const { return ctor(node->ctor, std::make_index_sequence{}); } /** * @brief Returns a range to use to visit top-level data. * @return An iterable range to use to visit top-level data. */ [[nodiscard]] meta_range data() const ENTT_NOEXCEPT { return node->data; } /** * @brief Returns the data associated with a given identifier. * * The data of the base classes will also be visited, if any. * * @param id Unique identifier. * @return The data associated with the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { return internal::meta_visit<&node_type::data>([id](const auto* curr) { return curr->id == id; }, node); } /** * @brief Returns a range to use to visit top-level functions. * @return An iterable range to use to visit top-level functions. */ [[nodiscard]] meta_range func() const ENTT_NOEXCEPT { return node->func; } /** * @brief Returns the function associated with a given identifier. * * The functions of the base classes will also be visited, if any.
* In the case of overloaded functions, the first one with the required * identifier will be returned. * * @param id Unique identifier. * @return The function associated with the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { return internal::meta_visit<&node_type::func>([id](const auto* curr) { return curr->id == id; }, node); } /** * @brief Creates an instance of the underlying type, if possible. * * Parameters must be such that a cast or conversion to the required types * is possible. Otherwise, an empty and thus invalid wrapper is returned. * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any* const args, const size_type sz) const { meta_any ret{}; internal::meta_visit<&node_type::ctor>([args, sz, &ret](const auto* curr) { return (curr->arity == sz) && (ret = curr->invoke(args)); }, node); return ret; } /** * @copybrief construct * * @sa construct * * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return construct(arguments, sizeof...(Args)); } /** * @brief Invokes a function given an identifier, if possible. * * It must be possible to cast the instance to the parent type of the member * function. Otherwise, invoking the underlying function results in an * undefined behavior. * * @sa meta_func::invoke * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any* const args, const size_type sz) const { const internal::meta_func_node* candidate{}; size_type extent{ sz + 1u }; bool ambiguous{}; for (auto* it = internal::meta_visit<&node_type::func>([id, sz](const auto* curr) { return curr->id == id && curr->arity == sz; }, node); it && it->id == id && it->arity == sz; it = it->next) { size_type direct{}; size_type ext{}; for (size_type next{}; next < sz && next == (direct + ext); ++next) { const auto type = args[next].type(); const auto req = it->arg(next).info(); type.info() == req ? ++direct : (ext += can_cast_or_convert(type.node, req)); } if ((direct + ext) == sz) { if (ext < extent) { candidate = it; extent = ext; ambiguous = false; } else if (ext == extent) { ambiguous = true; } } } return (candidate && !ambiguous) ? candidate->invoke(std::move(instance), args) : meta_any{}; } /** * @copybrief invoke * * @sa invoke * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the new instance, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&... args) const { meta_any arguments[sizeof...(Args) + 1u]{ std::forward(args)... }; return invoke(id, std::move(instance), arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the setter results in an undefined * behavior.
* 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 instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, meta_handle instance, Type&& value) const { const auto candidate = data(id); return candidate && candidate.set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * * It must be possible to cast the instance to the parent type of the data * member. Otherwise, invoking the getter results in an undefined behavior. * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); return candidate ? candidate.get(std::move(instance)) : meta_any{}; } /** * @brief Returns a range to use to visit top-level properties. * @return An iterable range to use to visit top-level properties. */ [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { return node->prop; } /** * @brief Returns the property associated with a given key. * * Properties of the base classes will also be visited, if any. * * @param key The key to use to search for a property. * @return The property associated with the given key, if any. */ [[nodiscard]] meta_prop prop(meta_any key) const { return internal::meta_visit<&node_type::prop>([&key](const auto* curr) { return curr->id == key; }, node); } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(node == nullptr); } /** * @brief Checks if two objects refer to the same type. * @param other The object with which to compare. * @return True if the objects refer to the same type, false otherwise. */ [[nodiscard]] bool operator==(const meta_type& other) const ENTT_NOEXCEPT { return (!node && !other.node) || (node && other.node && node->info == other.node->info); } /** * @brief Resets a type and all its parts. * * This function resets a type and all its data members, member functions * and properties, as well as its constructors, destructors and conversion * functions if any.
* Base classes aren't reset but the link between the two types is removed. * * The type is also removed from the list of searchable types. */ void reset() ENTT_NOEXCEPT { for (auto** it = internal::meta_context::global(); *it; it = &(*it)->next) { if (*it == node) { *it = (*it)->next; break; } } unregister_all(&node->prop); unregister_all(&node->base); unregister_all(&node->conv); unregister_all<&internal::meta_ctor_node::prop>(&node->ctor); unregister_all<&internal::meta_data_node::prop>(&node->data); unregister_all<&internal::meta_func_node::prop>(&node->func); node->id = {}; node->ctor = node->def_ctor; node->dtor = nullptr; } private: node_type* node; }; /** * @brief Checks if two objects refer to the same type. * @param lhs An object, either valid or not. * @param rhs An object, either valid or not. * @return False if the objects refer to the same node, true otherwise. */ [[nodiscard]] inline bool operator!=(const meta_type& lhs, const meta_type& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } [[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { return node; } template meta_any meta_any::invoke(const id_type id, Args &&... args) const { return type().invoke(id, *this, std::forward(args)...); } template meta_any meta_any::invoke(const id_type id, Args &&... args) { return type().invoke(id, *this, std::forward(args)...); } template bool meta_any::set(const id_type id, Type&& value) { return type().set(id, *this, std::forward(value)); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) const { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) { return type().get(id, *this); } [[nodiscard]] inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } [[nodiscard]] inline meta_type meta_data::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_data::type() const ENTT_NOEXCEPT { return node->type(); } [[nodiscard]] inline meta_type meta_func::parent() const ENTT_NOEXCEPT { return node->parent; } [[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { return node->ret(); } [[nodiscard]] inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { return index < arity() ? node->arg(index) : meta_type{}; } /*! @brief Opaque iterator for sequence containers. */ class meta_sequence_container::meta_iterator { /*! @brief A sequence container can access the underlying iterator. */ friend class meta_sequence_container; enum class operation { INCR, DEREF }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable(const operation op, const any& from, void* to) { switch (op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: static_cast(to)->emplace::reference>(*any_cast(from)); break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = meta_any; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs a meta iterator from a given iterator. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(It iter) : vtable{ &basic_vtable }, handle{ std::move(iter) } {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator& operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), * this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator& other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { meta_any other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type* vtable{}; any handle{}; }; template struct meta_sequence_container::meta_sequence_container_proxy { using traits_type = meta_sequence_container_traits; [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any& container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool resize(any& container, size_type sz) { auto* const cont = any_cast(&container); return cont && traits_type::resize(*cont, sz); } [[nodiscard]] static bool clear(any& container) { auto* const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ traits_type::begin(*cont) }; } return iterator{ traits_type::cbegin(any_cast(container)) }; } [[nodiscard]] static iterator end(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ traits_type::end(*cont) }; } return iterator{ traits_type::cend(any_cast(container)) }; } [[nodiscard]] static std::pair insert(any& container, iterator it, meta_any& value) { if (auto* const cont = any_cast(&container); cont) { // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector if (value.allow_cast() || value.allow_cast()) { const auto* element = value.try_cast>(); auto ret = traits_type::insert(*cont, any_cast(it.handle), element ? *element : value.cast()); return { iterator{std::move(ret.first)}, ret.second }; } } return {}; } [[nodiscard]] static std::pair erase(any& container, iterator it) { if (auto* const cont = any_cast(&container); cont) { auto ret = traits_type::erase(*cont, any_cast(it.handle)); return { iterator{std::move(ret.first)}, ret.second }; } return {}; } [[nodiscard]] static meta_any get(any& container, size_type pos) { if (auto* const cont = any_cast(&container); cont) { return meta_any{ std::in_place_type, traits_type::get(*cont, pos) }; } return meta_any{ std::in_place_type, traits_type::cget(any_cast(container), pos) }; } }; /** * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ [[nodiscard]] inline meta_type meta_sequence_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /** * @brief Returns the size of a container. * @return The size of the container. */ [[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /** * @brief Resizes a container to contain a given number of elements. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::resize(size_type sz) { return resize_fn(storage, sz); } /** * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { return clear_fn(storage); } /** * @brief Returns an iterator to the first element of a container. * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { return begin_fn(storage); } /** * @brief Returns an iterator that is past the last element of a container. * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { return end_fn(storage); } /** * @brief Inserts an element at a specified location of a container. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A pair consisting of an iterator to the inserted element (in case of * success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::insert(iterator it, meta_any value) { return insert_fn(storage, it, value); } /** * @brief Removes a given element from a container. * @param it Iterator to the element to remove. * @return A pair consisting of an iterator following the last removed element * (in case of success) and a bool denoting whether the insertion took place. */ inline std::pair meta_sequence_container::erase(iterator it) { return erase_fn(storage, it); } /** * @brief Returns a reference to the element at a given location of a container * (no bounds checking is performed). * @param pos The position of the element to return. * @return A reference to the requested element properly wrapped. */ [[nodiscard]] inline meta_any meta_sequence_container::operator[](size_type pos) { return get_fn(storage, pos); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } /*! @brief Opaque iterator for associative containers. */ class meta_associative_container::meta_iterator { enum operation { INCR, DEREF }; using vtable_type = void(const operation, const any&, void*); template static void basic_vtable(const operation op, const any& from, void* to) { switch (op) { case operation::INCR: ++any_cast(const_cast(from)); break; case operation::DEREF: const auto& it = any_cast(from); if constexpr (KeyOnly) { static_cast*>(to)->first.emplace(*it); } else { static_cast*>(to)->first.emplacefirst))>(it->first); static_cast*>(to)->second.emplacesecond))>(it->second); } break; } } public: /*! @brief Signed integer type. */ using difference_type = std::ptrdiff_t; /*! @brief Type of elements returned by the iterator. */ using value_type = std::pair; /*! @brief Pointer type, `void` on purpose. */ using pointer = void; /*! @brief Reference type, it is **not** an actual reference. */ using reference = value_type; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ meta_iterator() ENTT_NOEXCEPT = default; /** * @brief Constructs an meta iterator from a given iterator. * @tparam KeyOnly True if the container is also key-only, false otherwise. * @tparam It Type of actual iterator with which to build the meta iterator. * @param iter The actual iterator with which to build the meta iterator. */ template meta_iterator(std::integral_constant, It iter) : vtable{ &basic_vtable }, handle{ std::move(iter) } {} /*! @brief Pre-increment operator. @return This iterator. */ meta_iterator& operator++() ENTT_NOEXCEPT { return vtable(operation::INCR, handle, nullptr), * this; } /*! @brief Post-increment operator. @return This iterator. */ meta_iterator operator++(int) ENTT_NOEXCEPT { meta_iterator orig = *this; return ++(*this), orig; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return True if the iterators refer to the same element, false otherwise. */ [[nodiscard]] bool operator==(const meta_iterator& other) const ENTT_NOEXCEPT { return handle == other.handle; } /** * @brief Checks if two iterators refer to the same element. * @param other The iterator with which to compare. * @return False if the iterators refer to the same element, true otherwise. */ [[nodiscard]] bool operator!=(const meta_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } /** * @brief Indirection operator. * @return The element to which the iterator points. */ [[nodiscard]] reference operator*() const { reference other; vtable(operation::DEREF, handle, &other); return other; } /** * @brief Returns false if an iterator is invalid, true otherwise. * @return False if the iterator is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(handle); } private: vtable_type* vtable{}; any handle{}; }; template struct meta_associative_container::meta_associative_container_proxy { using traits_type = meta_associative_container_traits; [[nodiscard]] static meta_type key_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static meta_type mapped_type() ENTT_NOEXCEPT { if constexpr (is_key_only_meta_associative_container_v) { return meta_type{}; } else { return internal::meta_info::resolve(); } } [[nodiscard]] static meta_type value_type() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } [[nodiscard]] static size_type size(const any& container) ENTT_NOEXCEPT { return traits_type::size(any_cast(container)); } [[nodiscard]] static bool clear(any& container) { auto* const cont = any_cast(&container); return cont && traits_type::clear(*cont); } [[nodiscard]] static iterator begin(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::begin(*cont) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cbegin(any_cast(container)) }; } [[nodiscard]] static iterator end(any& container) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::end(*cont) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cend(any_cast(container)) }; } [[nodiscard]] static bool insert(any& container, meta_any& key, meta_any& value) { if (auto* const cont = any_cast(&container); cont && key.allow_cast()) { if constexpr (is_key_only_meta_associative_container_v) { return traits_type::insert(*cont, key.cast()); } else { return value.allow_cast() && traits_type::insert(*cont, key.cast(), value.cast()); } } return false; } [[nodiscard]] static bool erase(any& container, meta_any& key) { if (auto* const cont = any_cast(&container); cont && key.allow_cast()) { return traits_type::erase(*cont, key.cast()); } return false; } [[nodiscard]] static iterator find(any& container, meta_any& key) { if (key.allow_cast()) { if (auto* const cont = any_cast(&container); cont) { return iterator{ is_key_only_meta_associative_container{}, traits_type::find(*cont, key.cast()) }; } return iterator{ is_key_only_meta_associative_container{}, traits_type::cfind(any_cast(container), key.cast()) }; } return {}; } }; /** * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ [[nodiscard]] inline bool meta_associative_container::key_only() const ENTT_NOEXCEPT { return key_only_container; } /** * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { return key_type_fn(); } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { return mapped_type_fn(); } /*! @copydoc meta_sequence_container::value_type */ [[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { return value_type_fn(); } /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { return begin_fn(storage); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { return end_fn(storage); } /** * @brief Inserts an element (a key/value pair) into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { return insert_fn(storage, key, value); } /** * @brief Removes the specified element from a container. * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ inline bool meta_associative_container::erase(meta_any key) { return erase_fn(storage, key); } /** * @brief Returns an iterator to the element with a given key, if any. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { return find_fn(storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { return static_cast(storage); } } #endif // #include "meta/node.hpp" #ifndef ENTT_META_NODE_HPP #define ENTT_META_NODE_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; struct meta_handle; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct meta_prop_node { meta_prop_node* next; const meta_any& id; meta_any& value; }; struct meta_base_node { meta_type_node* const parent; meta_base_node* next; meta_type_node* (* const type)() ENTT_NOEXCEPT; const void* (* const cast)(const void*) ENTT_NOEXCEPT; }; struct meta_conv_node { meta_type_node* const parent; meta_conv_node* next; meta_type_node* (* const type)() ENTT_NOEXCEPT; meta_any(* const conv)(const void*); }; struct meta_ctor_node { using size_type = std::size_t; meta_type_node* const parent; meta_ctor_node* next; meta_prop_node* prop; const size_type arity; meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_any* const); }; struct meta_data_node { id_type id; meta_type_node* const parent; meta_data_node* next; meta_prop_node* prop; const bool is_const; const bool is_static; meta_type_node* (* const type)() ENTT_NOEXCEPT; bool(* const set)(meta_handle, meta_any); meta_any(* const get)(meta_handle); }; struct meta_func_node { using size_type = std::size_t; id_type id; meta_type_node* const parent; meta_func_node* next; meta_prop_node* prop; const size_type arity; const bool is_const; const bool is_static; meta_type_node* (* const ret)() ENTT_NOEXCEPT; meta_type(* const arg)(const size_type) ENTT_NOEXCEPT; meta_any(* const invoke)(meta_handle, meta_any*); }; struct meta_template_info { using size_type = std::size_t; const bool is_template_specialization; const size_type arity; meta_type_node* (* const type)() ENTT_NOEXCEPT; meta_type_node* (* const arg)(const size_type) ENTT_NOEXCEPT; }; struct meta_type_node { using size_type = std::size_t; const type_info info; id_type id; meta_type_node* next; meta_prop_node* prop; const size_type size_of; const bool is_void; const bool is_integral; const bool is_floating_point; const bool is_array; const bool is_enum; const bool is_union; const bool is_class; const bool is_pointer; const bool is_function_pointer; const bool is_member_object_pointer; const bool is_member_function_pointer; const bool is_pointer_like; const bool is_sequence_container; const bool is_associative_container; const meta_template_info template_info; const size_type rank; size_type(* const extent)(const size_type) ENTT_NOEXCEPT; meta_type_node* (* const remove_pointer)() ENTT_NOEXCEPT; meta_type_node* (* const remove_extent)() ENTT_NOEXCEPT; meta_ctor_node* const def_ctor; meta_ctor_node* ctor{ nullptr }; meta_base_node* base{ nullptr }; meta_conv_node* conv{ nullptr }; meta_data_node* data{ nullptr }; meta_func_node* func{ nullptr }; void(*dtor)(void*) { nullptr }; }; template auto meta_visit(const Op& op, const Node* node) -> std::decay_t*Member)> { for (auto* curr = node->*Member; curr; curr = curr->next) { if (op(curr)) { return curr; } } if constexpr (std::is_same_v) { for (auto* curr = node->base; curr; curr = curr->next) { if (auto* ret = meta_visit(op, curr->type()); ret) { return ret; } } } return nullptr; } template meta_type_node* meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; template class ENTT_API meta_node { static_assert(std::is_same_v>>, "Invalid type"); template [[nodiscard]] static auto extent(const meta_type_node::size_type dim, std::index_sequence) ENTT_NOEXCEPT { meta_type_node::size_type ext{}; ((ext = (dim == Index ? std::extent_v : ext)), ...); return ext; } [[nodiscard]] static meta_ctor_node* meta_default_constructor([[maybe_unused]] meta_type_node* type) ENTT_NOEXCEPT { if constexpr (std::is_default_constructible_v) { static meta_ctor_node node{ type, nullptr, nullptr, 0u, nullptr, [](meta_any* const) { return meta_any{std::in_place_type}; } }; return &node; } else { return nullptr; } } [[nodiscard]] static meta_template_info meta_template_descriptor() ENTT_NOEXCEPT { if constexpr (is_complete_v>) { return { true, meta_template_traits::args_type::size, &meta_node::class_type>::resolve, [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } }; } else { return { false, 0u, nullptr, nullptr }; } } public: [[nodiscard]] static meta_type_node* resolve() ENTT_NOEXCEPT { static meta_type_node node{ type_id(), {}, nullptr, nullptr, size_of_v, std::is_void_v, std::is_integral_v, std::is_floating_point_v, std::is_array_v, std::is_enum_v, std::is_union_v, std::is_class_v, std::is_pointer_v, std::is_pointer_v && std::is_function_v>, std::is_member_object_pointer_v, std::is_member_function_pointer_v, is_meta_pointer_like_v, is_complete_v>, is_complete_v>, meta_template_descriptor(), std::rank_v, [](meta_type_node::size_type dim) ENTT_NOEXCEPT { return extent(dim, std::make_index_sequence>{}); }, & meta_node>>>::resolve, & meta_node>>>::resolve, meta_default_constructor(&node), meta_default_constructor(&node) }; return &node; } }; template struct meta_info : meta_node>> {}; template meta_type_node* meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { meta_type_node* args[sizeof...(Args) + 1u]{ nullptr, internal::meta_info::resolve()... }; return args[index + 1u]; } } /** * Internal details not to be documented. * @endcond */ } #endif // #include "meta/pointer.hpp" #ifndef ENTT_META_POINTER_HPP #define ENTT_META_POINTER_HPP #include #include // #include "type_traits.hpp" namespace entt { /** * @brief Makes plain pointers pointer-like types for the meta system. * @tparam Type Element type. */ template struct is_meta_pointer_like : std::true_type {}; /** * @brief Partial specialization used to reject pointers to arrays. * @tparam Type Type of elements of the array. * @tparam N Number of elements of the array. */ template struct is_meta_pointer_like : std::false_type {}; /** * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta * system. * @tparam Type Element type. */ template struct is_meta_pointer_like> : std::true_type {}; /** * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta * system. * @tparam Type Element type. * @tparam Args Other arguments. */ template struct is_meta_pointer_like> : std::true_type {}; } #endif // #include "meta/policy.hpp" #ifndef ENTT_META_POLICY_HPP #define ENTT_META_POLICY_HPP namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ struct as_ref_t {}; /*! @brief Empty class type used to request the _as cref_ policy. */ struct as_cref_t {}; /*! @brief Empty class type used to request the _as-is_ policy. */ struct as_is_t {}; /*! @brief Empty class type used to request the _as void_ policy. */ struct as_void_t {}; } #endif // #include "meta/range.hpp" #ifndef ENTT_META_RANGE_HPP #define ENTT_META_RANGE_HPP #include #include namespace entt { /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. * @tparam Node Type of meta nodes iterated. */ template class meta_range { struct range_iterator { using difference_type = std::ptrdiff_t; using value_type = Type; using pointer = void; using reference = value_type; using iterator_category = std::input_iterator_tag; using node_type = Node; range_iterator() ENTT_NOEXCEPT = default; range_iterator(node_type* head) ENTT_NOEXCEPT : it{ head } {} range_iterator& operator++() ENTT_NOEXCEPT { return (it = it->next), * this; } range_iterator operator++(int) ENTT_NOEXCEPT { range_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { return it; } [[nodiscard]] bool operator==(const range_iterator& other) const ENTT_NOEXCEPT { return other.it == it; } [[nodiscard]] bool operator!=(const range_iterator& other) const ENTT_NOEXCEPT { return !(*this == other); } private: node_type* it{}; }; public: /*! @brief Node type. */ using node_type = Node; /*! @brief Input iterator type. */ using iterator = range_iterator; /*! @brief Default constructor. */ meta_range() ENTT_NOEXCEPT = default; /** * @brief Constructs a meta range from a given node. * @param head The underlying node with which to construct the range. */ meta_range(node_type* head) : node{ head } {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first meta object of the range. */ [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { return iterator{ node }; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last meta object of the * range. */ [[nodiscard]] iterator end() const ENTT_NOEXCEPT { return iterator{}; } private: node_type* node{ nullptr }; }; } #endif // #include "meta/resolve.hpp" #ifndef ENTT_META_RESOLVE_HPP #define ENTT_META_RESOLVE_HPP #include // #include "../core/type_info.hpp" // #include "ctx.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "range.hpp" namespace entt { /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. * @return The meta type associated with the given type, if any. */ template [[nodiscard]] meta_type resolve() ENTT_NOEXCEPT { return internal::meta_info::resolve(); } /** * @brief Returns a range to use to visit all meta types. * @return An iterable range to use to visit all meta types. */ [[nodiscard]] inline meta_range resolve() { return *internal::meta_context::global(); } /** * @brief Returns the meta type associated with a given identifier, if any. * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ [[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { for (auto* curr = *internal::meta_context::global(); curr; curr = curr->next) { if (curr->id == id) { return curr; } } return {}; } /** * @brief Returns the meta type associated with a given type info object, if * any. * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ [[nodiscard]] inline meta_type resolve(const type_info info) ENTT_NOEXCEPT { for (auto* curr = *internal::meta_context::global(); curr; curr = curr->next) { if (curr->info == info) { return curr; } } return {}; } } #endif // #include "meta/template.hpp" #ifndef ENTT_META_TEMPLATE_HPP #define ENTT_META_TEMPLATE_HPP // #include "../core/type_traits.hpp" namespace entt { /*! @brief Utility class to disambiguate class templates. */ template typename> struct meta_class_template_tag {}; /** * @brief General purpose traits class for generating meta template information. * @tparam Clazz Type of class template. * @tparam Args Types of template arguments. */ template typename Clazz, typename... Args> struct meta_template_traits> { /*! @brief Wrapped class template. */ using class_type = meta_class_template_tag; /*! @brief List of template arguments. */ using args_type = type_list; }; } #endif // #include "meta/type_traits.hpp" #ifndef ENTT_META_TYPE_TRAITS_HPP #define ENTT_META_TYPE_TRAITS_HPP #include namespace entt { /** * @brief Traits class template to be specialized to enable support for meta * template information. */ template struct meta_template_traits; /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. */ template struct meta_sequence_container_traits; /** * @brief Traits class template to be specialized to enable support for meta * associative containers. */ template struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a meta associative * container claims to wrap a key-only type, false otherwise. * @tparam Type Potentially key-only meta associative container type. */ template struct is_key_only_meta_associative_container : std::true_type {}; /*! @copydoc is_key_only_meta_associative_container */ template struct is_key_only_meta_associative_container::type::mapped_type>> : std::false_type {}; /** * @brief Helper variable template. * @tparam Type Potentially key-only meta associative container type. */ template inline constexpr auto is_key_only_meta_associative_container_v = is_key_only_meta_associative_container::value; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like : std::false_type {}; /** * @brief Partial specialization to ensure that const pointer-like types are * also accepted. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like : is_meta_pointer_like {}; /** * @brief Helper variable template. * @tparam Type Potentially pointer-like type. */ template inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; } #endif // #include "meta/utility.hpp" #ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "policy.hpp" namespace entt { /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = std::conditional_t, type_list, type_list>; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = true; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = !std::is_same_v; }; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = std::conditional_t, type_list, type_list>; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = false; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = !std::is_same_v; }; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Args Function arguments. */ template struct meta_function_descriptor { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = type_list; /*! @brief True if the meta function is const, false otherwise. */ static constexpr auto is_const = false; /*! @brief True if the meta function is static, false otherwise. */ static constexpr auto is_static = true; }; /** * @brief Meta function helper. * * Converts a function type to be associated with a reflected type into its meta * function descriptor. * * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template class meta_function_helper { template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class::*)(Args...) const); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(Class::*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret(*)(Args...)); public: /*! @brief The meta function descriptor of the given function. */ using type = decltype(get_rid_of_noexcept(std::declval())); }; /** * @brief Helper type. * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template using meta_function_helper_t = typename meta_function_helper::type; /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Args Actual types of arguments. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(type_list, const std::size_t index) ENTT_NOEXCEPT { return internal::meta_arg_node(type_list{}, index); } /** * @brief Constructs an instance given a list of erased parameters, if possible. * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @tparam Index Indexes to use to extract erased arguments from their list. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(meta_any* const args, std::index_sequence) { if (((args + Index)->allow_cast() && ...)) { return Type{ (args + Index)->cast()... }; } return {}; } /** * @brief Sets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to set. * @param instance An opaque instance of the underlying type, if required. * @param value Parameter to use to set the variable. * @return True in case of success, false otherwise. */ template [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { if constexpr (!std::is_same_v && !std::is_same_v) { if constexpr (std::is_function_v>>) { using data_type = type_list_element_t<1u, typename meta_function_helper_t::args_type>; if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { Data(*clazz, value.cast()); return true; } } else if constexpr (std::is_member_function_pointer_v) { using data_type = type_list_element_t<0u, typename meta_function_helper_t::args_type>; if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { (clazz->*Data)(value.cast()); return true; } } else if constexpr (std::is_member_object_pointer_v) { using data_type = std::remove_reference_t().*Data)>; if constexpr (!std::is_array_v && !std::is_const_v) { if (auto* const clazz = instance->try_cast(); clazz && value.allow_cast()) { clazz->*Data = value.cast(); return true; } } } else { using data_type = std::remove_reference_t; if constexpr (!std::is_array_v && !std::is_const_v) { if (value.allow_cast()) { *Data = value.cast(); return true; } } } } return false; } /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param value Value to wrap. * @return A meta any containing the returned value. */ template meta_any meta_dispatch(Type&& value) { if constexpr (std::is_same_v) { return meta_any{ std::in_place_type, std::forward(value) }; } else if constexpr (std::is_same_v) { return meta_any{ std::in_place_type, std::forward(value) }; } else if constexpr (std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ std::in_place_type&>, std::as_const(value) }; } else { static_assert(std::is_same_v, "Policy not supported"); return meta_any{ std::forward(value) }; } } /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { if constexpr (std::is_function_v>>) { auto* const clazz = instance->try_cast, const Type, Type>>(); return clazz ? meta_dispatch(Data(*clazz)) : meta_any{}; } else if constexpr (std::is_member_function_pointer_v) { auto* const clazz = instance->try_cast, const Type, Type>>(); return clazz ? meta_dispatch((clazz->*Data)()) : meta_any{}; } else if constexpr (std::is_member_object_pointer_v) { if constexpr (!std::is_array_v().*Data)>>>) { if (auto* clazz = instance->try_cast(); clazz) { return meta_dispatch(clazz->*Data); } else if (auto* fallback = instance->try_cast(); fallback) { return meta_dispatch(fallback->*Data); } } return meta_any{}; } else if constexpr (std::is_pointer_v) { if constexpr (std::is_array_v>) { return meta_any{}; } else { return meta_dispatch(*Data); } } else { return meta_dispatch(Data); } } /** * @brief Invokes a function given a list of erased parameters, if possible. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @tparam Index Indexes to use to extract erased arguments from their list. * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke([[maybe_unused]] meta_handle instance, meta_any* args, std::index_sequence) { using descriptor = meta_function_helper_t; const auto invoke = [](auto&& maybe_clazz, auto &&... other) { if constexpr (std::is_member_function_pointer_v) { if constexpr (std::is_void_v) { (std::forward(maybe_clazz).*Candidate)(std::forward(other)...); return meta_any{ std::in_place_type }; } else { return meta_dispatch((std::forward(maybe_clazz).*Candidate)(std::forward(other)...)); } } else { if constexpr (std::is_void_v) { Candidate(std::forward(maybe_clazz), std::forward(other)...); return meta_any{ std::in_place_type }; } else { return meta_dispatch(Candidate(std::forward(maybe_clazz), std::forward(other)...)); } } }; if constexpr (std::is_invocable_v...>) { if (const auto* const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return invoke(*clazz, (args + Index)->cast>()...); } } else if constexpr (std::is_invocable_v...>) { if (auto* const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return invoke(*clazz, (args + Index)->cast>()...); } } else { if (((args + Index)->allow_cast>() && ...)) { return invoke((args + Index)->cast>()...); } } return meta_any{}; } /** * @brief Invokes a function given a list of erased parameters, if possible. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @tparam Index Indexes to use to extract erased arguments from their list. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle, meta_any*, std::index_sequence) { if constexpr (std::is_void_v) { Candidate(); return meta_any{ std::in_place_type }; } else { return meta_dispatch(Candidate()); } } } #endif // #include "platform/android-ndk-r17.hpp" #ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP #define ENTT_PLATFORM_ANDROID_NDK_R17_HPP /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ #ifdef __ANDROID__ #include #if __NDK_MAJOR__ == 17 #include #include #include namespace std { namespace internal { template constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval(), std::declval()...), std::true_type{}); template constexpr std::false_type is_invocable(...); template constexpr auto is_invocable_r(int) ->std::enable_if_t(), std::declval()...)), Ret>, std::true_type > ; template constexpr std::false_type is_invocable_r(...); } template struct is_invocable : decltype(internal::is_invocable(0)) {}; template inline constexpr bool is_invocable_v = std::is_invocable::value; template struct is_invocable_r : decltype(internal::is_invocable_r(0)) {}; template inline constexpr bool is_invocable_r_v = std::is_invocable_r::value; template struct invoke_result { using type = decltype(std::invoke(std::declval(), std::declval()...)); }; template using invoke_result_t = typename std::invoke_result::type; } #endif #endif /** * Internal details not to be documented. * @endcond */ #endif // #include "poly/poly.hpp" #ifndef ENTT_POLY_POLY_HPP #define ENTT_POLY_POLY_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type&& operator()(Type&& value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::* member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func* func) ENTT_NOEXCEPT { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded : Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) ->overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ y_combinator(Func recursive) : func{ std::move(recursive) } {} /** * @brief Invokes a y-combinator and therefore its underlying function. * @tparam Args Types of arguments to use to invoke the underlying function. * @param args Parameters to use to invoke the underlying function. * @return Return value of the underlying function, if any. */ template decltype(auto) operator()(Args &&... args) const { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template decltype(auto) operator()(Args &&... args) { return func(*this, std::forward(args)...); } private: Func func; }; } #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif namespace entt { /** * @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 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; using vtable_type = const void* (const operation, const basic_any&, void*); template static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; template [[nodiscard]] static constexpr policy type_to_policy() { if constexpr (std::is_lvalue_reference_v) { if constexpr (std::is_const_v>) { return policy::CREF; } else { return policy::REF; } } else { return policy::OWNER; } } template [[nodiscard]] static bool compare(const void* lhs, const void* rhs) { if constexpr (!std::is_function_v && is_equality_comparable_v) { return *static_cast(lhs) == *static_cast(rhs); } else { return lhs == rhs; } } template 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>, Type>, "Invalid type"); if constexpr (!std::is_void_v) { const Type* instance = (in_situ && from.mode == policy::OWNER) ? ENTT_LAUNDER(reinterpret_cast(&from.storage)) : static_cast(from.instance); switch (op) { case operation::COPY: if constexpr (std::is_copy_constructible_v) { static_cast(to)->emplace(*instance); } break; case operation::MOVE: if constexpr (in_situ) { if (from.mode == policy::OWNER) { return new (&static_cast(to)->storage) Type{ std::move(*const_cast(instance)) }; } } return (static_cast(to)->instance = std::exchange(const_cast(from).instance, nullptr)); case operation::DTOR: if (from.mode == policy::OWNER) { if constexpr (in_situ) { instance->~Type(); } else if constexpr (std::is_array_v) { delete[] instance; } else { delete instance; } } break; case operation::COMP: return compare(instance, (*static_cast(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(to) = type_id(); break; } } return nullptr; } template void initialize([[maybe_unused]] Args &&... args) { if constexpr (!std::is_void_v) { if constexpr (std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); instance = (std::addressof(args), ...); } else if constexpr (in_situ) { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { new (&storage) Type{ std::forward(args)... }; } else { new (&storage) Type(std::forward(args)...); } } else { if constexpr (sizeof...(Args) != 0u && std::is_aggregate_v) { instance = new Type{ std::forward(args)... }; } else { instance = new Type(std::forward(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 }, 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 explicit basic_any(std::in_place_type_t, Args &&... args) : instance{}, vtable{ &basic_vtable>> }, mode{ type_to_policy() } { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper 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 basic_any(std::reference_wrapper value) ENTT_NOEXCEPT : basic_any{} { // invokes deprecated assignment operator (and avoids issues with vs2017) *this = value; } /** * @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, basic_any>>> basic_any(Type&& value) : instance{}, vtable{ &basic_vtable> }, mode{ policy::OWNER } { initialize>(std::forward(value)); } /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any& other) : instance{}, vtable{ &basic_vtable }, 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 [[deprecated("Use std::in_place_type, entt::make_any, emplace or forward_as_any instead")]] basic_any& operator=(std::reference_wrapper value) ENTT_NOEXCEPT { emplace(value.get()); 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 std::enable_if_t, basic_any>, basic_any&> operator=(Type&& value) { emplace>(std::forward(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(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 void emplace(Args &&... args) { std::exchange(vtable, &basic_vtable>>)(operation::DTOR, *this, nullptr); mode = type_to_policy(); initialize(std::forward(args)...); } /*! @brief Destroys contained object */ void reset() { std::exchange(vtable, &basic_vtable)(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; }; /** * @brief Checks if two wrappers differ in their content. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param lhs A wrapper, either empty or not. * @param rhs A wrapper, either empty or not. * @return True if the two wrappers differ in their content, false otherwise. */ template [[nodiscard]] inline bool operator!=(const basic_any& lhs, const basic_any& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @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 Type any_cast(const basic_any& data) ENTT_NOEXCEPT { const auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any&& data) ENTT_NOEXCEPT { // forces const on non-reference types to make them work also with wrappers for const references auto* const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } /*! @copydoc any_cast */ template const Type* any_cast(const basic_any* data) ENTT_NOEXCEPT { return (data->type() == type_id() ? static_cast(data->data()) : nullptr); } /*! @copydoc any_cast */ template Type* any_cast(basic_any* data) ENTT_NOEXCEPT { // last attempt to make wrappers for const references return their values return (data->type() == type_id() ? static_cast(static_cast, Type> *>(data)->data()) : nullptr); } /** * @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::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&... args) { return basic_any{std::in_place_type, std::forward(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::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type&& value) { return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; } } #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "hashed_string.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "fwd.hpp" #ifndef ENTT_POLY_FWD_HPP #define ENTT_POLY_FWD_HPP #include namespace entt { template)> class basic_poly; /** * @brief Alias declaration for the most common use case. * @tparam Concept Concept descriptor. */ template using poly = basic_poly; } #endif namespace entt { /*! @brief Inspector class used to infer the type of the virtual table. */ struct poly_inspector { /** * @brief Generic conversion operator (definition only). * @tparam Type Type to which conversion is requested. */ template operator Type && () const; /** * @brief Dummy invocation function (definition only). * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param args The arguments to pass to the function. * @return A poly inspector convertible to any type. */ template poly_inspector invoke(Args &&... args) const; /*! @copydoc invoke */ template poly_inspector invoke(Args &&... args); }; /** * @brief Static virtual table factory. * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. */ template class poly_vtable { using inspector = typename Concept::template type; template static auto vtable_entry(Ret(*)(inspector&, Args...))->Ret(*)(basic_any&, Args...); template static auto vtable_entry(Ret(*)(const inspector&, Args...))->Ret(*)(const basic_any&, Args...); template static auto vtable_entry(Ret(*)(Args...))->Ret(*)(const basic_any&, Args...); template static auto vtable_entry(Ret(inspector::*)(Args...))->Ret(*)(basic_any&, Args...); template static auto vtable_entry(Ret(inspector::*)(Args...) const)->Ret(*)(const basic_any&, Args...); template static auto make_vtable(value_list) -> decltype(std::make_tuple(vtable_entry(Candidate)...)); template [[nodiscard]] static constexpr auto make_vtable(type_list) { if constexpr (sizeof...(Func) == 0) { return decltype(make_vtable(typename Concept::template impl{})){}; } else if constexpr ((std::is_function_v && ...)) { return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; } } template static void fill_vtable_entry(Ret(*&entry)(Any&, Args...)) { if constexpr (std::is_invocable_r_v) { entry = +[](Any&, Args... args) -> Ret { return std::invoke(Candidate, std::forward(args)...); }; } else { entry = +[](Any& instance, Args... args) -> Ret { return static_cast(std::invoke(Candidate, any_cast&>(instance), std::forward(args)...)); }; } } template [[nodiscard]] static auto fill_vtable(std::index_sequence) { type impl{}; (fill_vtable_entry>>(std::get(impl)), ...); return impl; } public: /*! @brief Virtual table type. */ using type = decltype(make_vtable(Concept{})); /** * @brief Returns a static virtual table for a specific concept and type. * @tparam Type The type for which to generate the virtual table. * @return A static virtual table for the given concept and type. */ template [[nodiscard]] static const auto* instance() { static_assert(std::is_same_v>, "Type differs from its decayed form"); static const auto vtable = fill_vtable(std::make_index_sequence::size>{}); return &vtable; } }; /** * @brief Poly base class used to inject functionalities into concepts. * @tparam Poly The outermost poly class. */ template struct poly_base { /** * @brief Invokes a function from the static virtual table. * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template [[nodiscard]] decltype(auto) invoke(const poly_base& self, Args &&... args) const { const auto& poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } /*! @copydoc invoke */ template [[nodiscard]] decltype(auto) invoke(poly_base& self, Args &&... args) { auto& poly = static_cast(self); return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } }; /** * @brief Shortcut for calling `poly_base::invoke`. * @tparam Member Index of the function to invoke. * @tparam Poly A fully defined poly object. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template decltype(auto) poly_call(Poly&& self, Args &&... args) { return std::forward(self).template invoke(self, std::forward(args)...); } /** * @brief Static polymorphism made simple and within everyone's reach. * * Static polymorphism is a very powerful tool in C++, albeit sometimes * cumbersome to obtain.
* This class aims to make it simple and easy to use. * * @note * Both deduced and defined static virtual tables are supported.
* Moreover, the `poly` class template also works with unmanaged objects. * * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_poly : private Concept::template type>> { /*! @brief A poly base is allowed to snoop into a poly object. */ friend struct poly_base; using vtable_type = typename poly_vtable::type; public: /*! @brief Concept type. */ using concept_type = typename Concept::template type>; /*! @brief Default constructor. */ basic_poly() ENTT_NOEXCEPT : storage{}, vtable{} {} /** * @brief Constructs a poly by directly initializing the new object. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_poly(std::in_place_type_t, Args &&... args) : storage{ std::in_place_type, std::forward(args)... }, vtable{ poly_vtable::template instance>>() } {} /** * @brief Constructs a poly from a given value. * @tparam Type Type of object to use to initialize the poly. * @param value An instance of an object to use to initialize the poly. */ template>, basic_poly>>> basic_poly(Type&& value) ENTT_NOEXCEPT : basic_poly{ std::in_place_type>>, std::forward(value) } {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_poly(const basic_poly& other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ basic_poly(basic_poly&& other) ENTT_NOEXCEPT : basic_poly{} { swap(*this, other); } /** * @brief Assignment operator. * @param other The instance to assign from. * @return This poly object. */ basic_poly& operator=(basic_poly other) { swap(other, *this); 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 { return storage.type(); } /** * @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 Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&... args) { *this = basic_poly{ std::in_place_type, std::forward(args)... }; } /*! @brief Destroys contained object */ void reset() { *this = basic_poly{}; } /** * @brief Returns false if a poly is empty, true otherwise. * @return False if the poly is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return !(vtable == nullptr); } /** * @brief Returns a pointer to the underlying concept. * @return A pointer to the underlying concept. */ [[nodiscard]] concept_type* operator->() ENTT_NOEXCEPT { return this; } /*! @copydoc operator-> */ [[nodiscard]] const concept_type* operator->() const ENTT_NOEXCEPT { return this; } /** * @brief Swaps two poly objects. * @param lhs A valid poly object. * @param rhs A valid poly object. */ friend void swap(basic_poly& lhs, basic_poly& rhs) { using std::swap; swap(lhs.storage, rhs.storage); swap(lhs.vtable, rhs.vtable); } /** * @brief Aliasing constructor. * @return A poly that shares a reference to an unmanaged object. */ [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { basic_poly ref = std::as_const(*this).as_ref(); ref.storage = storage.as_ref(); return ref; } /*! @copydoc as_ref */ [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; return ref; } private: basic_any storage; const vtable_type* vtable; }; } #endif // #include "process/process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /** * @brief Base class for processes. * * This class stays true to the CRTP idiom. Derived classes must specify what's * the intended type for elapsed times.
* A process should expose publicly the following member functions whether * required: * * * @code{.cpp} * void update(Delta, void *); * @endcode * * It's invoked once per tick until a process is explicitly aborted or it * terminates either with or without errors. Even though it's not mandatory to * declare this member function, as a rule of thumb each process should at * least define it to work properly. The `void *` parameter is an opaque * pointer to user data (if any) forwarded directly to the process during an * update. * * * @code{.cpp} * void init(); * @endcode * * It's invoked when the process joins the running queue of a scheduler. This * happens as soon as it's attached to the scheduler if the process is a top * level one, otherwise when it replaces its parent if the process is a * continuation. * * * @code{.cpp} * void succeeded(); * @endcode * * It's invoked in case of success, immediately after an update and during the * same tick. * * * @code{.cpp} * void failed(); * @endcode * * It's invoked in case of errors, immediately after an update and during the * same tick. * * * @code{.cpp} * void aborted(); * @endcode * * It's invoked only if a process is explicitly aborted. There is no guarantee * that it executes in the same tick, this depends solely on whether the * process is aborted immediately or not. * * Derived classes can change the internal state of a process by invoking the * `succeed` and `fail` protected member functions and even pause or unpause the * process itself. * * @sa scheduler * * @tparam Derived Actual type of process that extends the class template. * @tparam Delta Type to use to provide elapsed time. */ template class process { enum class state : unsigned int { UNINITIALIZED = 0, RUNNING, PAUSED, SUCCEEDED, FAILED, ABORTED, FINISHED, REJECTED }; template auto next(std::integral_constant) -> decltype(std::declval().init(), void()) { static_cast(this)->init(); } template auto next(std::integral_constant, Delta delta, void* data) -> decltype(std::declval().update(delta, data), void()) { static_cast(this)->update(delta, data); } template auto next(std::integral_constant) -> decltype(std::declval().succeeded(), void()) { static_cast(this)->succeeded(); } template auto next(std::integral_constant) -> decltype(std::declval().failed(), void()) { static_cast(this)->failed(); } template auto next(std::integral_constant) -> decltype(std::declval().aborted(), void()) { static_cast(this)->aborted(); } void next(...) const ENTT_NOEXCEPT {} protected: /** * @brief Terminates a process with success if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. */ void succeed() ENTT_NOEXCEPT { if (alive()) { current = state::SUCCEEDED; } } /** * @brief Terminates a process with errors if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. */ void fail() ENTT_NOEXCEPT { if (alive()) { current = state::FAILED; } } /** * @brief Stops a process if it's in a running state. * * The function is idempotent and it does nothing if the process isn't * running. */ void pause() ENTT_NOEXCEPT { if (current == state::RUNNING) { current = state::PAUSED; } } /** * @brief Restarts a process if it's paused. * * The function is idempotent and it does nothing if the process isn't * paused. */ void unpause() ENTT_NOEXCEPT { if (current == state::PAUSED) { current = state::RUNNING; } } public: /*! @brief Type used to provide elapsed time. */ using delta_type = Delta; /*! @brief Default destructor. */ virtual ~process() { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } /** * @brief Aborts a process if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. * * @param immediately Requests an immediate operation. */ void abort(const bool immediately = false) { if (alive()) { current = state::ABORTED; if (immediately) { tick({}); } } } /** * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ [[nodiscard]] bool alive() const ENTT_NOEXCEPT { return current == state::RUNNING || current == state::PAUSED; } /** * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ [[nodiscard]] bool finished() const ENTT_NOEXCEPT { return current == state::FINISHED; } /** * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ [[nodiscard]] bool paused() const ENTT_NOEXCEPT { return current == state::PAUSED; } /** * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { return current == state::REJECTED; } /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void tick(const Delta delta, void* data = nullptr) { switch (current) { case state::UNINITIALIZED: next(std::integral_constant{}); current = state::RUNNING; break; case state::RUNNING: next(std::integral_constant{}, delta, data); break; default: // suppress warnings break; } // if it's dead, it must be notified and removed immediately switch (current) { case state::SUCCEEDED: next(std::integral_constant{}); current = state::FINISHED; break; case state::FAILED: next(std::integral_constant{}); current = state::REJECTED; break; case state::ABORTED: next(std::integral_constant{}); current = state::REJECTED; break; default: // suppress warnings break; } } private: state current{ state::UNINITIALIZED }; }; /** * @brief Adaptor for lambdas and functors to turn them into processes. * * Lambdas and functors can't be used directly with a scheduler for they are not * properly defined processes with managed life cycles.
* This class helps in filling the gap and turning lambdas and functors into * full featured processes usable by a scheduler. * * The signature of the function call operator should be equivalent to the * following: * * @code{.cpp} * void(Delta delta, void *data, auto succeed, auto fail); * @endcode * * Where: * * * `delta` is the elapsed time. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. * * `succeed` is a function to call when a process terminates with success. * * `fail` is a function to call when a process terminates with errors. * * The signature of the function call operator of both `succeed` and `fail` * is equivalent to the following: * * @code{.cpp} * void(); * @endcode * * Usually users shouldn't worry about creating adaptors. A scheduler will * create them internally each and avery time a lambda or a functor is used as * a process. * * @sa process * @sa scheduler * * @tparam Func Actual type of process. * @tparam Delta Type to use to provide elapsed time. */ template struct process_adaptor : process, Delta>, private Func { /** * @brief Constructs a process adaptor from a lambda or a functor. * @tparam Args Types of arguments to use to initialize the actual process. * @param args Parameters to use to initialize the actual process. */ template process_adaptor(Args &&... args) : Func{ std::forward(args)... } {} /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void update(const Delta delta, void* data) { Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); } }; } #endif // #include "process/scheduler.hpp" #ifndef ENTT_PROCESS_SCHEDULER_HPP #define ENTT_PROCESS_SCHEDULER_HPP #include #include #include #include #include // #include "../config/config.h" // #include "process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP #include #include // #include "../config/config.h" namespace entt { /** * @brief Base class for processes. * * This class stays true to the CRTP idiom. Derived classes must specify what's * the intended type for elapsed times.
* A process should expose publicly the following member functions whether * required: * * * @code{.cpp} * void update(Delta, void *); * @endcode * * It's invoked once per tick until a process is explicitly aborted or it * terminates either with or without errors. Even though it's not mandatory to * declare this member function, as a rule of thumb each process should at * least define it to work properly. The `void *` parameter is an opaque * pointer to user data (if any) forwarded directly to the process during an * update. * * * @code{.cpp} * void init(); * @endcode * * It's invoked when the process joins the running queue of a scheduler. This * happens as soon as it's attached to the scheduler if the process is a top * level one, otherwise when it replaces its parent if the process is a * continuation. * * * @code{.cpp} * void succeeded(); * @endcode * * It's invoked in case of success, immediately after an update and during the * same tick. * * * @code{.cpp} * void failed(); * @endcode * * It's invoked in case of errors, immediately after an update and during the * same tick. * * * @code{.cpp} * void aborted(); * @endcode * * It's invoked only if a process is explicitly aborted. There is no guarantee * that it executes in the same tick, this depends solely on whether the * process is aborted immediately or not. * * Derived classes can change the internal state of a process by invoking the * `succeed` and `fail` protected member functions and even pause or unpause the * process itself. * * @sa scheduler * * @tparam Derived Actual type of process that extends the class template. * @tparam Delta Type to use to provide elapsed time. */ template class process { enum class state : unsigned int { UNINITIALIZED = 0, RUNNING, PAUSED, SUCCEEDED, FAILED, ABORTED, FINISHED, REJECTED }; template auto next(std::integral_constant) -> decltype(std::declval().init(), void()) { static_cast(this)->init(); } template auto next(std::integral_constant, Delta delta, void* data) -> decltype(std::declval().update(delta, data), void()) { static_cast(this)->update(delta, data); } template auto next(std::integral_constant) -> decltype(std::declval().succeeded(), void()) { static_cast(this)->succeeded(); } template auto next(std::integral_constant) -> decltype(std::declval().failed(), void()) { static_cast(this)->failed(); } template auto next(std::integral_constant) -> decltype(std::declval().aborted(), void()) { static_cast(this)->aborted(); } void next(...) const ENTT_NOEXCEPT {} protected: /** * @brief Terminates a process with success if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. */ void succeed() ENTT_NOEXCEPT { if (alive()) { current = state::SUCCEEDED; } } /** * @brief Terminates a process with errors if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. */ void fail() ENTT_NOEXCEPT { if (alive()) { current = state::FAILED; } } /** * @brief Stops a process if it's in a running state. * * The function is idempotent and it does nothing if the process isn't * running. */ void pause() ENTT_NOEXCEPT { if (current == state::RUNNING) { current = state::PAUSED; } } /** * @brief Restarts a process if it's paused. * * The function is idempotent and it does nothing if the process isn't * paused. */ void unpause() ENTT_NOEXCEPT { if (current == state::PAUSED) { current = state::RUNNING; } } public: /*! @brief Type used to provide elapsed time. */ using delta_type = Delta; /*! @brief Default destructor. */ virtual ~process() { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } /** * @brief Aborts a process if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. * * @param immediately Requests an immediate operation. */ void abort(const bool immediately = false) { if (alive()) { current = state::ABORTED; if (immediately) { tick({}); } } } /** * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ [[nodiscard]] bool alive() const ENTT_NOEXCEPT { return current == state::RUNNING || current == state::PAUSED; } /** * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ [[nodiscard]] bool finished() const ENTT_NOEXCEPT { return current == state::FINISHED; } /** * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ [[nodiscard]] bool paused() const ENTT_NOEXCEPT { return current == state::PAUSED; } /** * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { return current == state::REJECTED; } /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void tick(const Delta delta, void* data = nullptr) { switch (current) { case state::UNINITIALIZED: next(std::integral_constant{}); current = state::RUNNING; break; case state::RUNNING: next(std::integral_constant{}, delta, data); break; default: // suppress warnings break; } // if it's dead, it must be notified and removed immediately switch (current) { case state::SUCCEEDED: next(std::integral_constant{}); current = state::FINISHED; break; case state::FAILED: next(std::integral_constant{}); current = state::REJECTED; break; case state::ABORTED: next(std::integral_constant{}); current = state::REJECTED; break; default: // suppress warnings break; } } private: state current{ state::UNINITIALIZED }; }; /** * @brief Adaptor for lambdas and functors to turn them into processes. * * Lambdas and functors can't be used directly with a scheduler for they are not * properly defined processes with managed life cycles.
* This class helps in filling the gap and turning lambdas and functors into * full featured processes usable by a scheduler. * * The signature of the function call operator should be equivalent to the * following: * * @code{.cpp} * void(Delta delta, void *data, auto succeed, auto fail); * @endcode * * Where: * * * `delta` is the elapsed time. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. * * `succeed` is a function to call when a process terminates with success. * * `fail` is a function to call when a process terminates with errors. * * The signature of the function call operator of both `succeed` and `fail` * is equivalent to the following: * * @code{.cpp} * void(); * @endcode * * Usually users shouldn't worry about creating adaptors. A scheduler will * create them internally each and avery time a lambda or a functor is used as * a process. * * @sa process * @sa scheduler * * @tparam Func Actual type of process. * @tparam Delta Type to use to provide elapsed time. */ template struct process_adaptor : process, Delta>, private Func { /** * @brief Constructs a process adaptor from a lambda or a functor. * @tparam Args Types of arguments to use to initialize the actual process. * @param args Parameters to use to initialize the actual process. */ template process_adaptor(Args &&... args) : Func{ std::forward(args)... } {} /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void update(const Delta delta, void* data) { Func::operator()(delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); } }; } #endif namespace entt { /** * @brief Cooperative scheduler for processes. * * A cooperative scheduler runs processes and helps managing their life cycles. * * Each process is invoked once per tick. If a process terminates, it's * removed automatically from the scheduler and it's never invoked again.
* A process can also have a child. In this case, the process is replaced with * its child when it terminates if it returns with success. In case of errors, * both the process and its child are discarded. * * Example of use (pseudocode): * * @code{.cpp} * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { * // code * }).then(arguments...); * @endcode * * In order to invoke all scheduled processes, call the `update` member function * passing it the elapsed time to forward to the tasks. * * @sa process * * @tparam Delta Type to use to provide elapsed time. */ template class scheduler { struct process_handler { using instance_type = std::unique_ptr; using update_fn_type = bool(process_handler&, Delta, void*); using abort_fn_type = void(process_handler&, bool); using next_type = std::unique_ptr; instance_type instance; update_fn_type* update; abort_fn_type* abort; next_type next; }; struct continuation { continuation(process_handler* ref) : handler{ ref } {} template continuation then(Args &&... args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); auto proc = typename process_handler::instance_type{ new Proc{std::forward(args)...}, &scheduler::deleter }; handler->next.reset(new process_handler{ std::move(proc), &scheduler::update, &scheduler::abort, nullptr }); handler = handler->next.get(); return *this; } template continuation then(Func&& func) { return then, Delta>>(std::forward(func)); } private: process_handler* handler; }; template [[nodiscard]] static bool update(process_handler& handler, const Delta delta, void* data) { auto* process = static_cast(handler.instance.get()); process->tick(delta, data); if (process->rejected()) { return true; } else if (process->finished()) { if (handler.next) { handler = std::move(*handler.next); // forces the process to exit the uninitialized state return handler.update(handler, {}, nullptr); } return true; } return false; } template static void abort(process_handler& handler, const bool immediately) { static_cast(handler.instance.get())->abort(immediately); } template static void deleter(void* proc) { delete static_cast(proc); } public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Default constructor. */ scheduler() = default; /*! @brief Default move constructor. */ scheduler(scheduler&&) = default; /*! @brief Default move assignment operator. @return This scheduler. */ scheduler& operator=(scheduler&&) = default; /** * @brief Number of processes currently scheduled. * @return Number of processes currently scheduled. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return handlers.size(); } /** * @brief Returns true if at least a process is currently scheduled. * @return True if there are scheduled processes, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return handlers.empty(); } /** * @brief Discards all scheduled processes. * * Processes aren't aborted. They are discarded along with their children * and never executed again. */ void clear() { handlers.clear(); } /** * @brief Schedules a process for the next tick. * * Returned value is an opaque object that can be used to attach a child to * the given process. The child is automatically scheduled when the process * terminates and only if the process returns with success. * * Example of use (pseudocode): * * @code{.cpp} * // schedules a task in the form of a process class * scheduler.attach(arguments...) * // appends a child in the form of a lambda function * .then([](auto delta, void *, auto succeed, auto fail) { * // code * }) * // appends a child in the form of another process class * .then(); * @endcode * * @tparam Proc Type of process to schedule. * @tparam Args Types of arguments to use to initialize the process. * @param args Parameters to use to initialize the process. * @return An opaque object to use to concatenate processes. */ template auto attach(Args &&... args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); auto proc = typename process_handler::instance_type{ new Proc{std::forward(args)...}, &scheduler::deleter }; process_handler handler{ std::move(proc), &scheduler::update, &scheduler::abort, nullptr }; // forces the process to exit the uninitialized state handler.update(handler, {}, nullptr); return continuation{ &handlers.emplace_back(std::move(handler)) }; } /** * @brief Schedules a process for the next tick. * * A process can be either a lambda or a functor. The scheduler wraps both * of them in a process adaptor internally.
* The signature of the function call operator should be equivalent to the * following: * * @code{.cpp} * void(Delta delta, void *data, auto succeed, auto fail); * @endcode * * Where: * * * `delta` is the elapsed time. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. * * `succeed` is a function to call when a process terminates with success. * * `fail` is a function to call when a process terminates with errors. * * The signature of the function call operator of both `succeed` and `fail` * is equivalent to the following: * * @code{.cpp} * void(); * @endcode * * Returned value is an opaque object that can be used to attach a child to * the given process. The child is automatically scheduled when the process * terminates and only if the process returns with success. * * Example of use (pseudocode): * * @code{.cpp} * // schedules a task in the form of a lambda function * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { * // code * }) * // appends a child in the form of another lambda function * .then([](auto delta, void *, auto succeed, auto fail) { * // code * }) * // appends a child in the form of a process class * .then(arguments...); * @endcode * * @sa process_adaptor * * @tparam Func Type of process to schedule. * @param func Either a lambda or a functor to use as a process. * @return An opaque object to use to concatenate processes. */ template auto attach(Func&& func) { using Proc = process_adaptor, Delta>; return attach(std::forward(func)); } /** * @brief Updates all scheduled processes. * * All scheduled processes are executed in no specific order.
* If a process terminates with success, it's replaced with its child, if * any. Otherwise, if a process terminates with an error, it's removed along * with its child. * * @param delta Elapsed time. * @param data Optional data. */ void update(const Delta delta, void* data = nullptr) { auto sz = handlers.size(); for (auto pos = handlers.size(); pos; --pos) { auto& handler = handlers[pos - 1]; if (const auto dead = handler.update(handler, delta, data); dead) { std::swap(handler, handlers[--sz]); } } handlers.erase(handlers.begin() + sz, handlers.end()); } /** * @brief Aborts all scheduled processes. * * Unless an immediate operation is requested, the abort is scheduled for * the next tick. Processes won't be executed anymore in any case.
* Once a process is fully aborted and thus finished, it's discarded along * with its child, if any. * * @param immediately Requests an immediate operation. */ void abort(const bool immediately = false) { decltype(handlers) exec; exec.swap(handlers); for (auto&& handler : exec) { handler.abort(handler, immediately); } std::move(handlers.begin(), handlers.end(), std::back_inserter(exec)); handlers.swap(exec); } private: std::vector handlers{}; }; } #endif // #include "resource/cache.hpp" #ifndef ENTT_RESOURCE_CACHE_HPP #define ENTT_RESOURCE_CACHE_HPP #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "handle.hpp" #ifndef ENTT_RESOURCE_HANDLE_HPP #define ENTT_RESOURCE_HANDLE_HPP #include #include #include // #include "../config/config.h" // #include "fwd.hpp" #ifndef ENTT_RESOURCE_FWD_HPP #define ENTT_RESOURCE_FWD_HPP namespace entt { template struct resource_cache; template class resource_handle; template class resource_loader; } #endif namespace entt { /** * @brief Shared resource handle. * * A shared resource handle is a small class that wraps a resource and keeps it * alive even if it's deleted from the cache. It can be either copied or * moved. A handle shares a reference to the same resource with all the other * handles constructed for the same identifier.
* As a rule of thumb, resources should never be copied nor moved. Handles are * the way to go to keep references to them. * * @tparam Resource Type of resource managed by a handle. */ template class resource_handle { /*! @brief Resource handles are friends with each other. */ template friend class resource_handle; public: /*! @brief Default constructor. */ resource_handle() ENTT_NOEXCEPT = default; /** * @brief Creates a handle from a shared pointer, namely a resource. * @param res A pointer to a properly initialized resource. */ resource_handle(std::shared_ptr res) ENTT_NOEXCEPT : resource{ std::move(res) } {} /** * @brief Copy constructor. * @param other The instance to copy from. */ resource_handle(const resource_handle& other) ENTT_NOEXCEPT = default; /** * @brief Move constructor. * @param other The instance to move from. */ resource_handle(resource_handle&& other) ENTT_NOEXCEPT = default; /** * @brief Copy constructs a handle which shares ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. */ template&& std::is_base_of_v>> resource_handle(const resource_handle& other) ENTT_NOEXCEPT : resource{ other.resource } {} /** * @brief Move constructs a handle which takes ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. */ template&& std::is_base_of_v>> resource_handle(resource_handle&& other) ENTT_NOEXCEPT : resource{ std::move(other.resource) } {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This resource handle. */ resource_handle& operator=(const resource_handle& other) ENTT_NOEXCEPT = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This resource handle. */ resource_handle& operator=(resource_handle&& other) ENTT_NOEXCEPT = default; /** * @brief Copy assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. * @return This resource handle. */ template std::enable_if_t&& std::is_base_of_v, resource_handle&> operator=(const resource_handle& other) ENTT_NOEXCEPT { resource = other.resource; return *this; } /** * @brief Move assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. * @return This resource handle. */ template std::enable_if_t&& std::is_base_of_v, resource_handle&> operator=(resource_handle&& other) ENTT_NOEXCEPT { resource = std::move(other.resource); return *this; } /** * @brief Gets a reference to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A reference to the managed resource. */ [[nodiscard]] const Resource& get() const ENTT_NOEXCEPT { ENTT_ASSERT(static_cast(resource), "Invalid resource"); return *resource; } /*! @copydoc get */ [[nodiscard]] Resource& get() ENTT_NOEXCEPT { return const_cast(std::as_const(*this).get()); } /*! @copydoc get */ [[nodiscard]] operator const Resource& () const ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] operator Resource& () ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] const Resource& operator *() const ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] Resource& operator *() ENTT_NOEXCEPT { return get(); } /** * @brief Gets a pointer to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A pointer to the managed resource or `nullptr` if the handle * contains no resource at all. */ [[nodiscard]] const Resource* operator->() const ENTT_NOEXCEPT { return resource.get(); } /*! @copydoc operator-> */ [[nodiscard]] Resource* operator->() ENTT_NOEXCEPT { return const_cast(std::as_const(*this).operator->()); } /** * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(resource); } private: std::shared_ptr resource; }; } #endif // #include "loader.hpp" #ifndef ENTT_RESOURCE_LOADER_HPP #define ENTT_RESOURCE_LOADER_HPP // #include "fwd.hpp" // #include "handle.hpp" namespace entt { /** * @brief Base class for resource loaders. * * Resource loaders must inherit from this class and stay true to the CRTP * idiom. Moreover, a resource loader must expose a public, const member * function named `load` that accepts a variable number of arguments and returns * a handle to the resource just created.
* As an example: * * @code{.cpp} * struct my_resource {}; * * struct my_loader: entt::resource_loader { * resource_handle load(int value) const { * // use the integer value somehow * return std::make_shared(); * } * }; * @endcode * * In general, resource loaders should not have a state or retain data of any * type. They should let the cache manage their resources instead. * * @note * Base class and CRTP idiom aren't strictly required with the current * implementation. One could argue that a cache can easily work with loaders of * any type. However, future changes won't be breaking ones by forcing the use * of a base class today and that's why the model is already in its place. * * @tparam Loader Type of the derived class. * @tparam Resource Type of resource for which to use the loader. */ template class resource_loader { /*! @brief Resource loaders are friends of their caches. */ template friend struct resource_cache; /** * @brief Loads the resource and returns it. * @tparam Args Types of arguments for the loader. * @param args Arguments for the loader. * @return The resource just loaded or an empty pointer in case of errors. */ template [[nodiscard]] resource_handle get(Args &&... args) const { return static_cast(this)->load(std::forward(args)...); } }; } #endif // #include "fwd.hpp" namespace entt { /** * @brief Simple cache for resources of a given type. * * Minimal implementation of a cache for resources of a given type. It doesn't * offer much functionalities but it's suitable for small or medium sized * applications and can be freely inherited to add targeted functionalities for * large sized applications. * * @tparam Resource Type of resources managed by a cache. */ template struct resource_cache { /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of resources managed by a cache. */ using resource_type = Resource; /*! @brief Default constructor. */ resource_cache() = default; /*! @brief Default move constructor. */ resource_cache(resource_cache&&) = default; /*! @brief Default move assignment operator. @return This cache. */ resource_cache& operator=(resource_cache&&) = default; /** * @brief Number of resources managed by a cache. * @return Number of resources currently stored. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return resources.size(); } /** * @brief Returns true if a cache contains no resources, false otherwise. * @return True if the cache contains no resources, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return resources.empty(); } /** * @brief Clears a cache and discards all its resources. * * Handles are not invalidated and the memory used by a resource isn't * freed as long as at least a handle keeps the resource itself alive. */ void clear() ENTT_NOEXCEPT { resources.clear(); } /** * @brief Loads the resource that corresponds to a given identifier. * * In case an identifier isn't already present in the cache, it loads its * resource and stores it aside for future uses. Arguments are forwarded * directly to the loader in order to construct properly the requested * resource. * * @note * If the identifier is already present in the cache, this function does * nothing and the arguments are simply discarded. * * @warning * If the resource cannot be loaded correctly, the returned handle will be * invalid and any use of it will result in undefined behavior. * * @tparam Loader Type of loader to use to load the resource if required. * @tparam Args Types of arguments to use to load the resource if required. * @param id Unique resource identifier. * @param args Arguments to use to load the resource if required. * @return A handle for the given resource. */ template resource_handle load(const id_type id, Args &&... args) { if (auto it = resources.find(id); it == resources.cend()) { if (auto handle = temp(std::forward(args)...); handle) { return (resources[id] = std::move(handle)); } } else { return it->second; } return {}; } /** * @brief Reloads a resource or loads it for the first time if not present. * * Equivalent to the following snippet (pseudocode): * * @code{.cpp} * cache.discard(id); * cache.load(id, args...); * @endcode * * Arguments are forwarded directly to the loader in order to construct * properly the requested resource. * * @warning * If the resource cannot be loaded correctly, the returned handle will be * invalid and any use of it will result in undefined behavior. * * @tparam Loader Type of loader to use to load the resource. * @tparam Args Types of arguments to use to load the resource. * @param id Unique resource identifier. * @param args Arguments to use to load the resource. * @return A handle for the given resource. */ template resource_handle reload(const id_type id, Args &&... args) { return (discard(id), load(id, std::forward(args)...)); } /** * @brief Creates a temporary handle for a resource. * * Arguments are forwarded directly to the loader in order to construct * properly the requested resource. The handle isn't stored aside and the * cache isn't in charge of the lifetime of the resource itself. * * @tparam Loader Type of loader to use to load the resource. * @tparam Args Types of arguments to use to load the resource. * @param args Arguments to use to load the resource. * @return A handle for the given resource. */ template [[nodiscard]] resource_handle temp(Args &&... args) const { return Loader{}.get(std::forward(args)...); } /** * @brief Creates a handle for a given resource identifier. * * A resource handle can be in a either valid or invalid state. In other * terms, a resource handle is properly initialized with a resource if the * cache contains the resource itself. Otherwise the returned handle is * uninitialized and accessing it results in undefined behavior. * * @sa resource_handle * * @param id Unique resource identifier. * @return A handle for the given resource. */ [[nodiscard]] resource_handle handle(const id_type id) const { if (auto it = resources.find(id); it != resources.cend()) { return it->second; } return {}; } /** * @brief Checks if a cache contains a given identifier. * @param id Unique resource identifier. * @return True if the cache contains the resource, false otherwise. */ [[nodiscard]] bool contains(const id_type id) const { return (resources.find(id) != resources.cend()); } /** * @brief Discards the resource that corresponds to a given identifier. * * Handles are not invalidated and the memory used by the resource isn't * freed as long as at least a handle keeps the resource itself alive. * * @param id Unique resource identifier. */ void discard(const id_type id) { if (auto it = resources.find(id); it != resources.end()) { resources.erase(it); } } /** * @brief Iterates all resources. * * The function object is invoked for each element. It is provided with * either the resource identifier, the resource handle or both of them.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entt::id_type); * void(entt::resource_handle); * void(const entt::id_type, entt::resource_handle); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { auto begin = resources.begin(); auto end = resources.end(); while (begin != end) { auto curr = begin++; if constexpr (std::is_invocable_v) { func(curr->first); } else if constexpr (std::is_invocable_v>) { func(curr->second); } else { func(curr->first, curr->second); } } } private: std::unordered_map> resources; }; } #endif // #include "resource/handle.hpp" #ifndef ENTT_RESOURCE_HANDLE_HPP #define ENTT_RESOURCE_HANDLE_HPP #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Shared resource handle. * * A shared resource handle is a small class that wraps a resource and keeps it * alive even if it's deleted from the cache. It can be either copied or * moved. A handle shares a reference to the same resource with all the other * handles constructed for the same identifier.
* As a rule of thumb, resources should never be copied nor moved. Handles are * the way to go to keep references to them. * * @tparam Resource Type of resource managed by a handle. */ template class resource_handle { /*! @brief Resource handles are friends with each other. */ template friend class resource_handle; public: /*! @brief Default constructor. */ resource_handle() ENTT_NOEXCEPT = default; /** * @brief Creates a handle from a shared pointer, namely a resource. * @param res A pointer to a properly initialized resource. */ resource_handle(std::shared_ptr res) ENTT_NOEXCEPT : resource{ std::move(res) } {} /** * @brief Copy constructor. * @param other The instance to copy from. */ resource_handle(const resource_handle& other) ENTT_NOEXCEPT = default; /** * @brief Move constructor. * @param other The instance to move from. */ resource_handle(resource_handle&& other) ENTT_NOEXCEPT = default; /** * @brief Copy constructs a handle which shares ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. */ template&& std::is_base_of_v>> resource_handle(const resource_handle& other) ENTT_NOEXCEPT : resource{ other.resource } {} /** * @brief Move constructs a handle which takes ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. */ template&& std::is_base_of_v>> resource_handle(resource_handle&& other) ENTT_NOEXCEPT : resource{ std::move(other.resource) } {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This resource handle. */ resource_handle& operator=(const resource_handle& other) ENTT_NOEXCEPT = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This resource handle. */ resource_handle& operator=(resource_handle&& other) ENTT_NOEXCEPT = default; /** * @brief Copy assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. * @return This resource handle. */ template std::enable_if_t&& std::is_base_of_v, resource_handle&> operator=(const resource_handle& other) ENTT_NOEXCEPT { resource = other.resource; return *this; } /** * @brief Move assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. * @return This resource handle. */ template std::enable_if_t&& std::is_base_of_v, resource_handle&> operator=(resource_handle&& other) ENTT_NOEXCEPT { resource = std::move(other.resource); return *this; } /** * @brief Gets a reference to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A reference to the managed resource. */ [[nodiscard]] const Resource& get() const ENTT_NOEXCEPT { ENTT_ASSERT(static_cast(resource), "Invalid resource"); return *resource; } /*! @copydoc get */ [[nodiscard]] Resource& get() ENTT_NOEXCEPT { return const_cast(std::as_const(*this).get()); } /*! @copydoc get */ [[nodiscard]] operator const Resource& () const ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] operator Resource& () ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] const Resource& operator *() const ENTT_NOEXCEPT { return get(); } /*! @copydoc get */ [[nodiscard]] Resource& operator *() ENTT_NOEXCEPT { return get(); } /** * @brief Gets a pointer to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A pointer to the managed resource or `nullptr` if the handle * contains no resource at all. */ [[nodiscard]] const Resource* operator->() const ENTT_NOEXCEPT { return resource.get(); } /*! @copydoc operator-> */ [[nodiscard]] Resource* operator->() ENTT_NOEXCEPT { return const_cast(std::as_const(*this).operator->()); } /** * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(resource); } private: std::shared_ptr resource; }; } #endif // #include "resource/loader.hpp" #ifndef ENTT_RESOURCE_LOADER_HPP #define ENTT_RESOURCE_LOADER_HPP // #include "fwd.hpp" // #include "handle.hpp" namespace entt { /** * @brief Base class for resource loaders. * * Resource loaders must inherit from this class and stay true to the CRTP * idiom. Moreover, a resource loader must expose a public, const member * function named `load` that accepts a variable number of arguments and returns * a handle to the resource just created.
* As an example: * * @code{.cpp} * struct my_resource {}; * * struct my_loader: entt::resource_loader { * resource_handle load(int value) const { * // use the integer value somehow * return std::make_shared(); * } * }; * @endcode * * In general, resource loaders should not have a state or retain data of any * type. They should let the cache manage their resources instead. * * @note * Base class and CRTP idiom aren't strictly required with the current * implementation. One could argue that a cache can easily work with loaders of * any type. However, future changes won't be breaking ones by forcing the use * of a base class today and that's why the model is already in its place. * * @tparam Loader Type of the derived class. * @tparam Resource Type of resource for which to use the loader. */ template class resource_loader { /*! @brief Resource loaders are friends of their caches. */ template friend struct resource_cache; /** * @brief Loads the resource and returns it. * @tparam Args Types of arguments for the loader. * @param args Arguments for the loader. * @return The resource just loaded or an empty pointer in case of errors. */ template [[nodiscard]] resource_handle get(Args &&... args) const { return static_cast(this)->load(std::forward(args)...); } }; } #endif // #include "signal/delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. /*! @cond TURN_OFF_DOXYGEN */ : choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of : std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_t = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_v = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Type First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = Type; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< std::disjunction_v...>, typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type> >; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other> : std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] constexpr bool is_equality_comparable(...) { return false; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<0>) -> decltype(std::declval() == std::declval()) { return true; } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<1>) -> decltype(std::declval(), std::declval() == std::declval()) { if constexpr (std::is_same_v) { return is_equality_comparable(choice<0>); } else { return is_equality_comparable(choice<2>); } } template [[nodiscard]] constexpr auto is_equality_comparable(choice_t<2>) -> decltype(std::declval(), std::declval() == std::declval()) { return is_equality_comparable(choice<2>) && is_equality_comparable(choice<2>); } } /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable : std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable> : std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r : std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r> : std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete : std::false_type {}; /*! @copydoc is_complete */ template struct is_complete> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator : std::false_type {}; /*! @copydoc is_iterator */ template struct is_iterator::iterator_category>> : std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is of the * required iterator type, false otherwise. * @tparam Type The type to test. * @tparam It Required iterator type. */ template struct is_iterator_type : std::false_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type&& std::is_same_v>> : std::true_type {}; /*! @copydoc is_iterator_type */ template struct is_iterator_type, std::void_t>> : is_iterator_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. * @tparam It Required iterator type. */ template inline constexpr bool is_iterator_type_v = is_iterator_type::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::add_const_t; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class* clazz(Ret(Class::*)(Args...)); template static Class* clazz(Ret(Class::*)(Args...) const); template static Class* clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; } #endif // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_NOEXCEPT noexcept # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_NOEXCEPT # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #if defined(__cpp_lib_launder) && __cpp_lib_launder >= 201606L # include # define ENTT_LAUNDER(expr) std::launder(expr) #else # define ENTT_LAUNDER(expr) expr #endif #ifndef ENTT_USE_ATOMIC # define ENTT_MAYBE_ATOMIC(Type) Type #else # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifdef ENTT_SPARSE_PAGE static_assert(ENTT_SPARSE_PAGE && ((ENTT_SPARSE_PAGE& (ENTT_SPARSE_PAGE - 1)) == 0), "ENTT_SPARSE_PAGE must be a power of two"); #else # define ENTT_SPARSE_PAGE 4096 #endif #ifdef ENTT_PACKED_PAGE static_assert(ENTT_PACKED_PAGE && ((ENTT_PACKED_PAGE& (ENTT_PACKED_PAGE - 1)) == 0), "ENTT_PACKED_PAGE must be a power of two"); #else # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(...) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, ...) assert(condition) #endif #ifdef ENTT_NO_ETO # include # define ENTT_IGNORE_IF_EMPTY std::false_type #else # include # define ENTT_IGNORE_IF_EMPTY std::true_type #endif #ifndef ENTT_STANDARD_CPP # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template auto function_pointer(Ret(*)(Args...))->Ret(*)(Args...); template auto function_pointer(Ret(*)(Type, Args...), Other&&)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...), Other &&...)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...) const, Other &&...)->Ret(*)(Args...); template auto function_pointer(Type Class::*, Other &&...)->Type(*)(); template using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { return std::index_sequence_for{}; } } /** * Internal details not to be documented. * @endcond */ /*! @brief Used to wrap a function or a member of a specified type. */ template struct connect_arg_t {}; /*! @brief Constant of type connect_arg_t used to disambiguate calls. */ template inline constexpr connect_arg_t connect_arg{}; /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void*, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type&, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type*, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void*, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() ENTT_NOEXCEPT : fn{ nullptr }, data{ nullptr } {} /** * @brief Constructs a delegate and connects a free function or an unbound * member. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ENTT_NOEXCEPT { connect(); } /** * @brief Constructs a delegate and connects a free function with payload or * a bound member. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template delegate(connect_arg_t, Type&& value_or_instance) ENTT_NOEXCEPT { connect(std::forward(value_or_instance)); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() ENTT_NOEXCEPT { data = nullptr; if constexpr (std::is_invocable_r_v) { fn = [](const void*, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr (std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type& value_or_instance) ENTT_NOEXCEPT { data = &value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type* value_or_instance) ENTT_NOEXCEPT { data = value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { fn = function; data = payload; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() ENTT_NOEXCEPT { fn = nullptr; data = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void* instance() const ENTT_NOEXCEPT { return data; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(data, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { // no need to test also data return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate& other) const ENTT_NOEXCEPT { return fn == other.fn && data == other.data; } private: function_type* fn; const void* data; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate& lhs, const delegate& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ->delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type&&) ->delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret(*)(const void*, Args...), const void* = nullptr) ->delegate; } #endif // #include "signal/dispatcher.hpp" #ifndef ENTT_SIGNAL_DISPATCHER_HPP #define ENTT_SIGNAL_DISPATCHER_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template)> class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; } /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifers in the codebase while using their numeric * counterparts at runtime.
* Because of that, a hashed string can also be used in constant expressions if * required. * * @tparam Char Character type. */ template class basic_hashed_string { using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char* curr) ENTT_NOEXCEPT: str{ curr } {} const Char* str; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr id_type helper(const Char* curr) ENTT_NOEXCEPT { auto value = traits_type::offset; while (*curr != 0) { value = (value ^ static_cast(*(curr++))) * traits_type::prime; } return value; } public: /*! @brief Character type. */ using value_type = Char; /*! @brief Unsigned integer type. */ using hash_type = id_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifer. * @param size Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type* str, std::size_t size) ENTT_NOEXCEPT { id_type partial{ traits_type::offset }; while (size--) { partial = (partial ^ (str++)[0]) * traits_type::prime; } return partial; } /** * @brief Returns directly the numeric representation of a string. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * const auto value = basic_hashed_string::to_value("my.png"); * @endcode * * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type(&str)[N]) ENTT_NOEXCEPT { return helper(str); } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() ENTT_NOEXCEPT : str{ nullptr }, hash{} {} /** * @brief Constructs a hashed string from an array of const characters. * * Forcing template resolution avoids implicit conversions. An * human-readable identifier can be anything but a plain, old bunch of * characters.
* Example of use: * @code{.cpp} * basic_hashed_string hs{"my.png"}; * @endcode * * @tparam N Number of characters of the identifier. * @param curr Human-readable identifer. */ template constexpr basic_hashed_string(const value_type(&curr)[N]) ENTT_NOEXCEPT : str{ curr }, hash{ helper(curr) } {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) ENTT_NOEXCEPT : str{ wrapper.str }, hash{ helper(wrapper.str) } {} /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the instance. */ [[nodiscard]] constexpr const value_type* data() const ENTT_NOEXCEPT { return str; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type* () const ENTT_NOEXCEPT { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ [[nodiscard]] constexpr operator hash_type() const ENTT_NOEXCEPT { return value(); } /** * @brief Compares two hashed strings. * @param other Hashed string with which to compare. * @return True if the two hashed strings are identical, false otherwise. */ [[nodiscard]] constexpr bool operator==(const basic_hashed_string& other) const ENTT_NOEXCEPT { return hash == other.hash; } private: const value_type* str; hash_type hash; }; /** * @brief Deduction guide. * * It allows to deduce the character type of the hashed string directly from a * human-readable identifer provided to the constructor. * * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifer. */ template basic_hashed_string(const Char(&str)[N]) ->basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string& lhs, const basic_hashed_string& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{ str }; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t* str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{ str }; } } } #endif // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_seq final { [[nodiscard]] static id_type next() ENTT_NOEXCEPT { static ENTT_MAYBE_ATOMIC(id_type) value {}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ ENTT_PRETTY_FUNCTION }; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{ "" }; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_seq final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() ENTT_NOEXCEPT { static const id_type value = internal::type_seq::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { return type_seq::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { return value(); } }; /*! @brief Implementation specific information about a type. */ class type_info final { template friend type_info type_id() ENTT_NOEXCEPT; type_info(id_type seq_v, id_type hash_v, std::string_view name_v) ENTT_NOEXCEPT : seq_value{ seq_v }, hash_value{ hash_v }, name_value{ name_v } {} public: /*! @brief Default constructor. */ type_info() ENTT_NOEXCEPT : type_info({}, {}, {}) {} /*! @brief Default copy constructor. */ type_info(const type_info&) ENTT_NOEXCEPT = default; /*! @brief Default move constructor. */ type_info(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Default copy assignment operator. * @return This type info object. */ type_info& operator=(const type_info&) ENTT_NOEXCEPT = default; /** * @brief Default move assignment operator. * @return This type info object. */ type_info& operator=(type_info&&) ENTT_NOEXCEPT = default; /** * @brief Checks if a type info object is properly initialized. * @return True if the object is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return name_value.data() != nullptr; } /** * @brief Type sequential identifier. * @return Type sequential identifier. */ [[nodiscard]] id_type seq() const ENTT_NOEXCEPT { return seq_value; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] id_type hash() const ENTT_NOEXCEPT { return hash_value; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] std::string_view name() const ENTT_NOEXCEPT { return name_value; } /** * @brief Compares the contents of two type info objects. * @param other Object with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const type_info& other) const ENTT_NOEXCEPT { return hash_value == other.hash_value; } private: id_type seq_value; id_type hash_value; std::string_view name_value; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two contents differ, false otherwise. */ [[nodiscard]] inline bool operator!=(const type_info& lhs, const type_info& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Returns the type info object for a given type. * @tparam Type Type for which to generate a type info object. * @return The type info object for the given type. */ template [[nodiscard]] type_info type_id() ENTT_NOEXCEPT { return type_info{ type_seq>>::value(), type_hash>>::value(), type_name>>::value() }; } } #endif // #include "sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../core/type_traits.hpp" // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template auto function_pointer(Ret(*)(Args...))->Ret(*)(Args...); template auto function_pointer(Ret(*)(Type, Args...), Other&&)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...), Other &&...)->Ret(*)(Args...); template auto function_pointer(Ret(Class::*)(Args...) const, Other &&...)->Ret(*)(Args...); template auto function_pointer(Type Class::*, Other &&...)->Type(*)(); template using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret(*)(Args...)) { return std::index_sequence_for{}; } } /** * Internal details not to be documented. * @endcond */ /*! @brief Used to wrap a function or a member of a specified type. */ template struct connect_arg_t {}; /*! @brief Constant of type connect_arg_t used to disambiguate calls. */ template inline constexpr connect_arg_t connect_arg{}; /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { return [](const void*, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type&, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type*, std::index_sequence) ENTT_NOEXCEPT { return [](const void* payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type* curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void*, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() ENTT_NOEXCEPT : fn{ nullptr }, data{ nullptr } {} /** * @brief Constructs a delegate and connects a free function or an unbound * member. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ENTT_NOEXCEPT { connect(); } /** * @brief Constructs a delegate and connects a free function with payload or * a bound member. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template delegate(connect_arg_t, Type&& value_or_instance) ENTT_NOEXCEPT { connect(std::forward(value_or_instance)); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() ENTT_NOEXCEPT { data = nullptr; if constexpr (std::is_invocable_r_v) { fn = [](const void*, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr (std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type& value_or_instance) ENTT_NOEXCEPT { data = &value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type* value_or_instance) ENTT_NOEXCEPT { data = value_or_instance; if constexpr (std::is_invocable_r_v) { fn = [](const void* payload, Args... args) -> Ret { Type* curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type* function, const void* payload = nullptr) ENTT_NOEXCEPT { fn = function; data = payload; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() ENTT_NOEXCEPT { fn = nullptr; data = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void* instance() const ENTT_NOEXCEPT { return data; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(data, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { // no need to test also data return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate& other) const ENTT_NOEXCEPT { return fn == other.fn && data == other.data; } private: function_type* fn; const void* data; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate& lhs, const delegate& rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) ->delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type&&) ->delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret(*)(const void*, Args...), const void* = nullptr) ->delegate; } #endif // #include "fwd.hpp" #ifndef ENTT_SIGNAL_FWD_HPP #define ENTT_SIGNAL_FWD_HPP namespace entt { template class delegate; class dispatcher; template class emitter; class connection; struct scoped_connection; template class sink; template class sigh; } #endif namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink; /** * @brief Instance type when it comes to connecting member functions. * @tparam Class Type of class to which the member function belongs. */ template using instance_type = Class*; /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for (auto&& call : std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for (auto&& call : calls) { if constexpr (std::is_void_v) { if constexpr (std::is_invocable_r_v) { call(args...); if (func()) { break; } } else { call(args...); func(); } } else { if constexpr (std::is_invocable_r_v) { if (func(call(args...))) { break; } } else { func(call(args...)); } } } } private: std::vector> calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void* ref) : disconnect{ fn }, signal{ ref } {} public: /*! @brief Default constructor. */ connection() = default; /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if (disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void* signal{}; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection& other) : conn{ other } {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection&) = delete; /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection& operator=(const scoped_connection&) = delete; /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection& operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sink { using signal_type = sigh; using difference_type = typename std::iterator_traits::difference_type; template static void release(Type value_or_instance, void* signal) { sink{ *static_cast(signal) }.disconnect(value_or_instance); } template static void release(void* signal) { sink{ *static_cast(signal) }.disconnect(); } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh& ref) ENTT_NOEXCEPT : offset{}, signal{ &ref } {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type&& value_or_instance) { delegate call{}; call.template connect(value_or_instance); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type& value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type* value_or_instance) { sink other{ *this }; if (value_or_instance) { const auto& calls = signal->calls; const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }); other.offset = std::distance(it, calls.cend()); } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{ *this }; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function or an unbound member to a signal. * * The signal handler performs checks to avoid multiple connections for the * same function. * * @tparam Candidate Function or member to connect to the signal. * @return A properly initialized connection object. */ template connection connect() { disconnect(); delegate call{}; call.template connect(); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(); return { std::move(conn), signal }; } /** * @brief Connects a free function with payload or a bound member to a * signal. * * The signal isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized connection object. */ template connection connect(Type&& value_or_instance) { disconnect(value_or_instance); delegate call{}; call.template connect(value_or_instance); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance); return { std::move(conn), signal }; } /** * @brief Disconnects a free function or an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. */ template void disconnect() { auto& calls = signal->calls; delegate call{}; call.template connect(); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects a free function with payload or a bound member from a * signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type&& value_or_instance) { auto& calls = signal->calls; delegate call{}; call.template connect(value_or_instance); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type& value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type* value_or_instance) { if (value_or_instance) { auto& calls = signal->calls; calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type* signal; }; /** * @brief Deduction guide. * * It allows to deduce the function type of a sink directly from the signal it * refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template sink(sigh&) ->sink; } #endif namespace entt { /** * @brief Basic dispatcher implementation. * * A dispatcher can be used either to trigger an immediate event or to enqueue * events to be published all together once per tick.
* Listeners are provided in the form of member functions. For each event of * type `Event`, listeners are such that they can be invoked with an argument of * type `Event &`, no matter what the return type is. * * The dispatcher creates instances of the `sigh` class internally. Refer to the * documentation of the latter for more details. */ class dispatcher { struct basic_pool { virtual ~basic_pool() = default; virtual void publish() = 0; virtual void disconnect(void*) = 0; virtual void clear() ENTT_NOEXCEPT = 0; }; template struct pool_handler final : basic_pool { static_assert(std::is_same_v>, "Invalid event type"); using signal_type = sigh; using sink_type = typename signal_type::sink_type; void publish() override { const auto length = events.size(); for (std::size_t pos{}; pos < length; ++pos) { signal.publish(events[pos]); } events.erase(events.cbegin(), events.cbegin() + length); } void disconnect(void* instance) override { sink().disconnect(instance); } void clear() ENTT_NOEXCEPT override { events.clear(); } [[nodiscard]] sink_type sink() ENTT_NOEXCEPT { return entt::sink{ signal }; } template void trigger(Args &&... args) { Event instance{ std::forward(args)... }; signal.publish(instance); } template void enqueue(Args &&... args) { if constexpr (std::is_aggregate_v) { events.push_back(Event{ std::forward(args)... }); } else { events.emplace_back(std::forward(args)...); } } private: signal_type signal{}; std::vector events; }; template [[nodiscard]] pool_handler& assure() { const auto index = type_seq::value(); if (!(index < pools.size())) { pools.resize(std::size_t(index) + 1u); } if (!pools[index]) { pools[index].reset(new pool_handler{}); } return static_cast &>(*pools[index]); } public: /*! @brief Default constructor. */ dispatcher() = default; /*! @brief Default move constructor. */ dispatcher(dispatcher&&) = default; /*! @brief Default move assignment operator. @return This dispatcher. */ dispatcher& operator=(dispatcher&&) = default; /** * @brief Returns a sink object for the given event. * * A sink is an opaque object used to connect listeners to events. * * The function type for a listener is _compatible_ with: * @code{.cpp} * void(Event &); * @endcode * * The order of invocation of the listeners isn't guaranteed. * * @sa sink * * @tparam Event Type of event of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto sink() { return assure().sink(); } /** * @brief Triggers an immediate event of the given type. * * All the listeners registered for the given type are immediately notified. * The event is discarded after the execution. * * @tparam Event Type of event to trigger. * @tparam Args Types of arguments to use to construct the event. * @param args Arguments to use to construct the event. */ template void trigger(Args &&... args) { assure().trigger(std::forward(args)...); } /** * @brief Triggers an immediate event of the given type. * * All the listeners registered for the given type are immediately notified. * The event is discarded after the execution. * * @tparam Event Type of event to trigger. * @param event An instance of the given type of event. */ template void trigger(Event&& event) { assure>().trigger(std::forward(event)); } /** * @brief Enqueues an event of the given type. * * An event of the given type is queued. No listener is invoked. Use the * `update` member function to notify listeners when ready. * * @tparam Event Type of event to enqueue. * @tparam Args Types of arguments to use to construct the event. * @param args Arguments to use to construct the event. */ template void enqueue(Args &&... args) { assure().enqueue(std::forward(args)...); } /** * @brief Enqueues an event of the given type. * * An event of the given type is queued. No listener is invoked. Use the * `update` member function to notify listeners when ready. * * @tparam Event Type of event to enqueue. * @param event An instance of the given type of event. */ template void enqueue(Event&& event) { assure>().enqueue(std::forward(event)); } /** * @brief Utility function to disconnect everything related to a given value * or instance from a dispatcher. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type& value_or_instance) { disconnect(&value_or_instance); } /** * @brief Utility function to disconnect everything related to a given value * or instance from a dispatcher. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type* value_or_instance) { for (auto&& cpool : pools) { if (cpool) { cpool->disconnect(value_or_instance); } } } /** * @brief Discards all the events queued so far. * * If no types are provided, the dispatcher will clear all the existing * pools. * * @tparam Event Type of events to discard. */ template void clear() { if constexpr (sizeof...(Event) == 0) { for (auto&& cpool : pools) { if (cpool) { cpool->clear(); } } } else { (assure().clear(), ...); } } /** * @brief Delivers all the pending events of the given type. * * This method is blocking and it doesn't return until all the events are * delivered to the registered listeners. It's responsibility of the users * to reduce at a minimum the time spent in the bodies of the listeners. * * @tparam Event Type of events to send. */ template void update() { assure().publish(); } /** * @brief Delivers all the pending events. * * This method is blocking and it doesn't return until all the events are * delivered to the registered listeners. It's responsibility of the users * to reduce at a minimum the time spent in the bodies of the listeners. */ void update() const { for (auto pos = pools.size(); pos; --pos) { if (auto&& cpool = pools[pos - 1]; cpool) { cpool->publish(); } } } private: std::vector> pools; }; } #endif // #include "signal/emitter.hpp" #ifndef ENTT_SIGNAL_EMITTER_HPP #define ENTT_SIGNAL_EMITTER_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" namespace entt { /** * @brief General purpose event emitter. * * The emitter class template follows the CRTP idiom. To create a custom emitter * type, derived classes must inherit directly from the base class as: * * @code{.cpp} * struct my_emitter: emitter { * // ... * } * @endcode * * Pools for the type of events are created internally on the fly. It's not * required to specify in advance the full list of accepted types.
* Moreover, whenever an event is published, an emitter provides the listeners * with a reference to itself along with a reference to the event. Therefore * listeners have an handy way to work with it without incurring in the need of * capturing a reference to the emitter. * * @tparam Derived Actual type of emitter that extends the class template. */ template class emitter { struct basic_pool { virtual ~basic_pool() = default; virtual bool empty() const ENTT_NOEXCEPT = 0; virtual void clear() ENTT_NOEXCEPT = 0; }; template struct pool_handler final : basic_pool { static_assert(std::is_same_v>, "Invalid event type"); using listener_type = std::function; using element_type = std::pair; using container_type = std::list; using connection_type = typename container_type::iterator; [[nodiscard]] bool empty() const ENTT_NOEXCEPT override { auto pred = [](auto&& element) { return element.first; }; return std::all_of(once_list.cbegin(), once_list.cend(), pred) && std::all_of(on_list.cbegin(), on_list.cend(), pred); } void clear() ENTT_NOEXCEPT override { if (publishing) { for (auto&& element : once_list) { element.first = true; } for (auto&& element : on_list) { element.first = true; } } else { once_list.clear(); on_list.clear(); } } connection_type once(listener_type listener) { return once_list.emplace(once_list.cend(), false, std::move(listener)); } connection_type on(listener_type listener) { return on_list.emplace(on_list.cend(), false, std::move(listener)); } void erase(connection_type conn) { conn->first = true; if (!publishing) { auto pred = [](auto&& element) { return element.first; }; once_list.remove_if(pred); on_list.remove_if(pred); } } void publish(Event& event, Derived& ref) { container_type swap_list; once_list.swap(swap_list); publishing = true; for (auto&& element : on_list) { element.first ? void() : element.second(event, ref); } for (auto&& element : swap_list) { element.first ? void() : element.second(event, ref); } publishing = false; on_list.remove_if([](auto&& element) { return element.first; }); } private: bool publishing{ false }; container_type once_list{}; container_type on_list{}; }; template [[nodiscard]] pool_handler* assure() { const auto index = type_seq::value(); if (!(index < pools.size())) { pools.resize(std::size_t(index) + 1u); } if (!pools[index]) { pools[index].reset(new pool_handler{}); } return static_cast *>(pools[index].get()); } template [[nodiscard]] const pool_handler* assure() const { const auto index = type_seq::value(); return (!(index < pools.size()) || !pools[index]) ? nullptr : static_cast *>(pools[index].get()); } public: /** @brief Type of listeners accepted for the given event. */ template using listener = typename pool_handler::listener_type; /** * @brief Generic connection type for events. * * Type of the connection object returned by the event emitter whenever a * listener for the given type is registered.
* It can be used to break connections still in use. * * @tparam Event Type of event for which the connection is created. */ template struct connection : private pool_handler::connection_type { /** @brief Event emitters are friend classes of connections. */ friend class emitter; /*! @brief Default constructor. */ connection() = default; /** * @brief Creates a connection that wraps its underlying instance. * @param conn A connection object to wrap. */ connection(typename pool_handler::connection_type conn) : pool_handler::connection_type{ std::move(conn) } {} }; /*! @brief Default constructor. */ emitter() = default; /*! @brief Default destructor. */ virtual ~emitter() { static_assert(std::is_base_of_v, Derived>, "Incorrect use of the class template"); } /*! @brief Default move constructor. */ emitter(emitter&&) = default; /*! @brief Default move assignment operator. @return This emitter. */ emitter& operator=(emitter&&) = default; /** * @brief Emits the given event. * * All the listeners registered for the specific event type are invoked with * the given event. The event type must either have a proper constructor for * the arguments provided or be an aggregate type. * * @tparam Event Type of event to publish. * @tparam Args Types of arguments to use to construct the event. * @param args Parameters to use to initialize the event. */ template void publish(Args &&... args) { Event instance{ std::forward(args)... }; assure()->publish(instance, *static_cast(this)); } /** * @brief Registers a long-lived listener with the event emitter. * * This method can be used to register a listener designed to be invoked * more than once for the given event type.
* The connection returned by the method can be freely discarded. It's meant * to be used later to disconnect the listener if required. * * The listener is as a callable object that can be moved and the type of * which is _compatible_ with `void(Event &, Derived &)`. * * @note * Whenever an event is emitted, the emitter provides the listener with a * reference to the derived class. Listeners don't have to capture those * instances for later uses. * * @tparam Event Type of event to which to connect the listener. * @param instance The listener to register. * @return Connection object that can be used to disconnect the listener. */ template connection on(listener instance) { return assure()->on(std::move(instance)); } /** * @brief Registers a short-lived listener with the event emitter. * * This method can be used to register a listener designed to be invoked * only once for the given event type.
* The connection returned by the method can be freely discarded. It's meant * to be used later to disconnect the listener if required. * * The listener is as a callable object that can be moved and the type of * which is _compatible_ with `void(Event &, Derived &)`. * * @note * Whenever an event is emitted, the emitter provides the listener with a * reference to the derived class. Listeners don't have to capture those * instances for later uses. * * @tparam Event Type of event to which to connect the listener. * @param instance The listener to register. * @return Connection object that can be used to disconnect the listener. */ template connection once(listener instance) { return assure()->once(std::move(instance)); } /** * @brief Disconnects a listener from the event emitter. * * Do not use twice the same connection to disconnect a listener, it results * in undefined behavior. Once used, discard the connection object. * * @tparam Event Type of event of the connection. * @param conn A valid connection. */ template void erase(connection conn) { assure()->erase(std::move(conn)); } /** * @brief Disconnects all the listeners for the given event type. * * All the connections previously returned for the given event are * invalidated. Using them results in undefined behavior. * * @tparam Event Type of event to reset. */ template void clear() { assure()->clear(); } /** * @brief Disconnects all the listeners. * * All the connections previously returned are invalidated. Using them * results in undefined behavior. */ void clear() ENTT_NOEXCEPT { for (auto&& cpool : pools) { if (cpool) { cpool->clear(); } } } /** * @brief Checks if there are listeners registered for the specific event. * @tparam Event Type of event to test. * @return True if there are no listeners registered, false otherwise. */ template [[nodiscard]] bool empty() const { const auto* cpool = assure(); return !cpool || cpool->empty(); } /** * @brief Checks if there are listeners registered with the event emitter. * @return True if there are no listeners registered, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return std::all_of(pools.cbegin(), pools.cend(), [](auto&& cpool) { return !cpool || cpool->empty(); }); } private: std::vector> pools{}; }; } #endif // #include "signal/sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "delegate.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Function A valid function type. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink; /** * @brief Instance type when it comes to connecting member functions. * @tparam Class Type of class to which the member function belongs. */ template using instance_type = Class*; /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for (auto&& call : std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for (auto&& call : calls) { if constexpr (std::is_void_v) { if constexpr (std::is_invocable_r_v) { call(args...); if (func()) { break; } } else { call(args...); func(); } } else { if constexpr (std::is_invocable_r_v) { if (func(call(args...))) { break; } } else { func(call(args...)); } } } } private: std::vector> calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void* ref) : disconnect{ fn }, signal{ ref } {} public: /*! @brief Default constructor. */ connection() = default; /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if (disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void* signal{}; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection& other) : conn{ other } {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection&) = delete; /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection& operator=(const scoped_connection&) = delete; /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection& operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class sink { using signal_type = sigh; using difference_type = typename std::iterator_traits::difference_type; template static void release(Type value_or_instance, void* signal) { sink{ *static_cast(signal) }.disconnect(value_or_instance); } template static void release(void* signal) { sink{ *static_cast(signal) }.disconnect(); } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh& ref) ENTT_NOEXCEPT : offset{}, signal{ &ref } {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type&& value_or_instance) { delegate call{}; call.template connect(value_or_instance); const auto& calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{ *this }; other.offset = std::distance(it, calls.cend()); return other; } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type& value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type* value_or_instance) { sink other{ *this }; if (value_or_instance) { const auto& calls = signal->calls; const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }); other.offset = std::distance(it, calls.cend()); } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{ *this }; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function or an unbound member to a signal. * * The signal handler performs checks to avoid multiple connections for the * same function. * * @tparam Candidate Function or member to connect to the signal. * @return A properly initialized connection object. */ template connection connect() { disconnect(); delegate call{}; call.template connect(); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(); return { std::move(conn), signal }; } /** * @brief Connects a free function with payload or a bound member to a * signal. * * The signal isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized connection object. */ template connection connect(Type&& value_or_instance) { disconnect(value_or_instance); delegate call{}; call.template connect(value_or_instance); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance); return { std::move(conn), signal }; } /** * @brief Disconnects a free function or an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. */ template void disconnect() { auto& calls = signal->calls; delegate call{}; call.template connect(); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects a free function with payload or a bound member from a * signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type&& value_or_instance) { auto& calls = signal->calls; delegate call{}; call.template connect(value_or_instance); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type& value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type* value_or_instance) { if (value_or_instance) { auto& calls = signal->calls; calls.erase(std::remove_if(calls.begin(), calls.end(), [value_or_instance](const auto& delegate) { return delegate.instance() == value_or_instance; }), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type* signal; }; /** * @brief Deduction guide. * * It allows to deduce the function type of a sink directly from the signal it * refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template sink(sigh&) ->sink; } #endif