# HG changeset patch # User David Demelier # Date 1633157116 -7200 # Node ID d87ff43cb2333b37297d9f1976da0dc1de9b4793 # Parent 35c644a88d1b95667782b4d59dda47c023d265e0 entt: upgrade to 3.8.1 diff -r 35c644a88d1b -r d87ff43cb233 VERSION.libentt.txt --- a/VERSION.libentt.txt Sat Oct 02 08:30:22 2021 +0200 +++ b/VERSION.libentt.txt Sat Oct 02 08:45:16 2021 +0200 @@ -1,1 +1,1 @@ -3.1.1 +3.8.1 diff -r 35c644a88d1b -r d87ff43cb233 libentt/entt/entt.hpp --- a/libentt/entt/entt.hpp Sat Oct 02 08:30:22 2021 +0200 +++ b/libentt/entt/entt.hpp Sat Oct 02 08:45:16 2021 +0200 @@ -1,8 +1,21 @@ +// #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 1 + + +#endif + // #include "core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP +#include #include #include #include @@ -12,54 +25,93 @@ #define ENTT_CORE_UTILITY_HPP +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC +#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 // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +# 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 @@ -75,37 +127,92 @@ * @return The submitted value as-is. */ template - constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** - * @brief Constant utility to disambiguate overloaded member functions. - * @tparam Type Function type of the desired overload. - * @tparam Class Type of class to which the member functions belong. - * @param member A valid pointer to a member function. - * @return Pointer to the member function. + * @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 -constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } +[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. - * @tparam Type Function type of the desired overload. + * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ -template -constexpr auto overload(Type *func) ENTT_NOEXCEPT { return func; } - - -} - - -#endif // ENTT_CORE_UTILITY_HPP +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 @@ -157,7 +264,7 @@ template> void operator()(It first, It last, Compare compare = Compare{}) const { if(first < last) { - for(auto it = first+1; it < last; ++it) { + for(auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; @@ -179,7 +286,7 @@ */ template struct radix_sort { - static_assert((N % Bit) == 0); + 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. @@ -210,17 +317,17 @@ std::size_t index[buckets]{}; std::size_t count[buckets]{}; - std::for_each(from, to, [&getter, &count, start](const value_type &item) { - ++count[(getter(item) >> start) & mask]; - }); - - std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { - item = *(index++) + *(count++); - }); - - std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { - out[index[(getter(item) >> start) & mask]++] = std::move(item); - }); + 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) { @@ -240,59 +347,298 @@ } -#endif // ENTT_CORE_ALGORITHM_HPP - -// #include "core/family.hpp" -#ifndef ENTT_CORE_FAMILY_HPP -#define ENTT_CORE_FAMILY_HPP - - -#include -// #include "../config/config.h" - - - -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(ENTT_ID_TYPE) identifier; - - template - // clang (since version 9) started to complain if auto is used instead of ENTT_ID_TYPE - inline static const ENTT_ID_TYPE inner = identifier++; - -public: - /*! @brief Unsigned integer type. */ - using family_type = ENTT_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 = inner...>; -}; - - -} - - -#endif // ENTT_CORE_FAMILY_HPP - -// #include "core/hashed_string.hpp" +#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 "../config/config.h" +#include +// #include "../config/config.h" + +// #include "fwd.hpp" @@ -314,6 +660,7 @@ template<> struct fnv1a_traits { + using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; @@ -321,6 +668,7 @@ template<> struct fnv1a_traits { + using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; @@ -331,7 +679,7 @@ /** * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN + * @endcond */ @@ -348,7 +696,7 @@ */ template class basic_hashed_string { - using traits_type = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -357,15 +705,33 @@ }; // Fowler–Noll–Vo hash function v. 1a - the good - static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT { - return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1); + [[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 = ENTT_ID_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. @@ -383,8 +749,8 @@ * @return The numeric representation of the string. */ template - static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT { - return helper(traits_type::offset, str); + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); } /** @@ -392,20 +758,8 @@ * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ - static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT { - return helper(traits_type::offset, wrapper.str); - } - - /** - * @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. - */ - static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - ENTT_ID_TYPE partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ @@ -429,7 +783,7 @@ */ template constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT - : str{curr}, hash{helper(traits_type::offset, curr)} + : str{curr}, hash{helper(curr)} {} /** @@ -438,14 +792,14 @@ * @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(traits_type::offset, wrapper.str)} + : 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. */ - constexpr const value_type * data() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { return str; } @@ -453,25 +807,25 @@ * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ - constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; } - - /*! @copydoc value */ - constexpr operator hash_type() 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. */ - constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { return hash == other.hash; } @@ -492,7 +846,7 @@ * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -504,7 +858,7 @@ * @return True if the two hashed strings are identical, false otherwise. */ template -constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } @@ -517,7 +871,7 @@ using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -525,7 +879,7 @@ * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -535,22 +889,1773 @@ * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } -#endif // ENTT_CORE_HASHED_STRING_HPP +} + + +} + + +#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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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 +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + +// #include "type_traits.hpp" @@ -586,36 +2691,35 @@ */ template class identifier { - using tuple_type = std::tuple...>; - - template - static constexpr ENTT_ID_TYPE get(std::index_sequence) ENTT_NOEXCEPT { - static_assert(std::disjunction_v...>); - return (0 + ... + (std::is_same_v> ? ENTT_ID_TYPE(Indexes) : ENTT_ID_TYPE{})); + 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 = ENTT_ID_TYPE; + using identifier_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template - static constexpr identifier_type type = get>(std::make_index_sequence{}); -}; - - -} - - -#endif // ENTT_CORE_IDENT_HPP + 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 -// #include "../config/config.h" +// #include "../config/config.h" + +// #include "fwd.hpp" @@ -633,7 +2737,7 @@ * both during an assignment and when they try to read back their data. * Otherwise, they can incur in unexpected results. */ -template +template struct monostate { /** * @brief Assigns a value of a specific type to a given key. @@ -665,77 +2769,1217 @@ * @brief Helper variable template. * @tparam Value Value used to differentiate between different variables. */ -template +template inline monostate monostate_v = {}; } -#endif // ENTT_CORE_MONOSTATE_HPP +#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 "../config/config.h" - -// #include "../core/hashed_string.hpp" -#ifndef ENTT_CORE_HASHED_STRING_HPP -#define ENTT_CORE_HASHED_STRING_HPP - - -#include +#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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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 -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC +#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 -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +#include +// #include "../config/config.h" @@ -751,271 +3995,3240 @@ namespace internal { -template -struct fnv1a_traits; - - -template<> -struct fnv1a_traits { - static constexpr std::uint32_t offset = 2166136261; - static constexpr std::uint32_t prime = 16777619; -}; - - -template<> -struct fnv1a_traits { - static constexpr std::uint64_t offset = 14695981039346656037ull; - static constexpr std::uint64_t prime = 1099511628211ull; -}; - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/** - * @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 - static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT { - return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1); - } - -public: - /*! @brief Character type. */ - using value_type = Char; - /*! @brief Unsigned integer type. */ - using hash_type = ENTT_ID_TYPE; - - /** - * @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 - static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT { - return helper(traits_type::offset, 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. - */ - static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT { - return helper(traits_type::offset, wrapper.str); - } - - /** - * @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. - */ - static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - ENTT_ID_TYPE partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; - } - - /*! @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(traits_type::offset, 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(traits_type::offset, wrapper.str)} - {} - - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - 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. - */ - constexpr hash_type value() const ENTT_NOEXCEPT { - return hash; - } - - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; } - - /*! @copydoc value */ - constexpr operator hash_type() const ENTT_NOEXCEPT { return hash; } - - /** - * @brief Compares two hashed strings. - * @param other Hashed string with which to compare. - * @return True if the two hashed strings are identical, false otherwise. - */ - constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { - return hash == other.hash; - } - -private: - const value_type *str; - hash_type hash; +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) | (static_cast(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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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) | (static_cast(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. - * - * 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]) ENTT_NOEXCEPT --> 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 -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; - - -} - - -/** - * @brief User defined literal for hashed strings. - * @param str The literal without its suffix. - * @return A properly initialized hashed string. - */ -constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(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. - */ -constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { - return entt::hashed_wstring{str}; -} - - -#endif // ENTT_CORE_HASHED_STRING_HPP - - - -namespace entt { - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. + * @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 static_cast(traits_type::to_entity(entt) / sparse_page); + } + + [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT { + return static_cast(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 = static_cast(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 = static_cast(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 : static_cast(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 static_cast(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 = static_cast(traits_type::to_entity(free_list)); + assure_page(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 = static_cast(traits_type::to_entity(entt)); + const auto to = static_cast(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_size> - : std::integral_constant -{}; - - -/** - * @brief Helper variable template. - * @tparam List Type list. - */ -template -constexpr auto type_list_size_v = type_list_size::value; +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. */ @@ -1100,342 +7313,3547 @@ using type_list_unique_t = typename type_list_unique::type; -/*! @brief Traits class used mainly to push things across boundaries. */ -template -struct named_type_traits; - - -/** - * @brief Specialization used to get rid of constness. - * @tparam Type Named type. - */ -template -struct named_type_traits - : named_type_traits -{}; - - -/** - * @brief Helper type. - * @tparam Type Potentially named type. - */ -template -using named_type_traits_t = typename named_type_traits::type; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - */ -template> -struct is_named_type: std::false_type {}; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - * @tparam Type Potentially named type. - */ -template -struct is_named_type>>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially named 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 -constexpr auto is_named_type_v = is_named_type::value; - - -/** - * @brief Defines an enum class to use for opaque identifiers and a dedicate - * `to_integer` function to convert the identifiers to their underlying type. - * @param clazz The name to use for the enum class. - * @param type The underlying type for the enum class. - */ -#define ENTT_OPAQUE_TYPE(clazz, type)\ - enum class clazz: type {};\ - constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ - return std::underlying_type_t(id);\ - } - - -} - - -/** - * @brief Utility macro to deal with an issue of MSVC. - * - * See _msvc-doesnt-expand-va-args-correctly_ on SO for all the details. - * - * @param args Argument to expand. - */ -#define ENTT_EXPAND(args) args - - -/** - * @brief Makes an already existing type a named type. - * - * The current definition contains a workaround for Clang 6 because it fails to - * deduce correctly the type to use to specialize the class template.
- * With a compiler that fully supports C++17 and works fine with deduction - * guides, the following should be fine instead: - * - * @code{.cpp} - * std::integral_constant - * @endcode - * - * In order to support even sligthly older compilers, I prefer to stick to the - * implementation below. - * - * @param type Type to assign a name to. - */ -#define ENTT_NAMED_TYPE(type)\ - template<>\ - struct entt::named_type_traits\ - : std::integral_constant>>>{#type}>\ - {\ - static_assert(std::is_same_v, type>);\ - }; - - -/** - * @brief Defines a named type (to use for structs). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_ONLY(clazz, body)\ - struct clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for structs). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { struct clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_STRUCT_OVERLOAD(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for structs). */ -#define ENTT_NAMED_STRUCT(...) ENTT_EXPAND(ENTT_NAMED_STRUCT_OVERLOAD(__VA_ARGS__, ENTT_NAMED_STRUCT_WITH_NAMESPACE, ENTT_NAMED_STRUCT_ONLY,)(__VA_ARGS__)) - - -/** - * @brief Defines a named type (to use for classes). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_ONLY(clazz, body)\ - class clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for classes). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { class clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_CLASS_MACRO(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for classes). */ -#define ENTT_NAMED_CLASS(...) ENTT_EXPAND(ENTT_NAMED_CLASS_MACRO(__VA_ARGS__, ENTT_NAMED_CLASS_WITH_NAMESPACE, ENTT_NAMED_CLASS_ONLY,)(__VA_ARGS__)) - - -#endif // ENTT_CORE_TYPE_TRAITS_HPP - -// #include "core/utility.hpp" - -// #include "entity/actor.hpp" -#ifndef ENTT_ENTITY_ACTOR_HPP -#define ENTT_ENTITY_ACTOR_HPP - - -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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{}, 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>().get({}))>, type_list<>, type_list>().end())>>...> + >; + using reverse_iterator = iterable_iterator< + typename basic_common_type::reverse_iterator, + type_list_cat_t>().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{}, 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 -// #include "../config/config.h" - -// #include "../core/family.hpp" -#ifndef ENTT_CORE_FAMILY_HPP -#define ENTT_CORE_FAMILY_HPP - - -#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 -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC +#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 // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - - - -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(ENTT_ID_TYPE) identifier; - - template - // clang (since version 9) started to complain if auto is used instead of ENTT_ID_TYPE - inline static const ENTT_ID_TYPE inner = identifier++; - -public: - /*! @brief Unsigned integer type. */ - using family_type = ENTT_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 = inner...>; -}; - - -} - - -#endif // ENTT_CORE_FAMILY_HPP - -// #include "../core/algorithm.hpp" -#ifndef ENTT_CORE_ALGORITHM_HPP -#define ENTT_CORE_ALGORITHM_HPP - - -#include -#include -#include -#include -// #include "utility.hpp" -#ifndef ENTT_CORE_UTILITY_HPP -#define ENTT_CORE_UTILITY_HPP - - -// #include "../config/config.h" +# 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 @@ -1451,235 +10869,149 @@ * @return The submitted value as-is. */ template - constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr Type && operator()(Type &&value) const ENTT_NOEXCEPT { return std::forward(value); } }; /** - * @brief Constant utility to disambiguate overloaded member functions. - * @tparam Type Function type of the desired overload. - * @tparam Class Type of class to which the member functions belong. - * @param member A valid pointer to a member function. - * @return Pointer to the member function. + * @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 -constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } +[[nodiscard]] constexpr auto overload(Type Class:: *member) ENTT_NOEXCEPT { return member; } /** * @brief Constant utility to disambiguate overloaded functions. - * @tparam Type Function type of the desired overload. + * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ -template -constexpr auto overload(Type *func) ENTT_NOEXCEPT { return func; } - - -} - - -#endif // ENTT_CORE_UTILITY_HPP - - - -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); - - /** - * @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]{}; - - std::for_each(from, to, [&getter, &count, start](const value_type &item) { - ++count[(getter(item) >> start) & mask]; - }); - - std::for_each(std::next(std::begin(index)), std::end(index), [index = std::begin(index), count = std::begin(count)](auto &item) mutable { - item = *(index++) + *(count++); - }); - - std::for_each(from, to, [&getter, &out, &index, start](value_type &item) { - out[index[(getter(item) >> start) & mask]++] = std::move(item); - }); - }; - - 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 // ENTT_CORE_ALGORITHM_HPP - -// #include "../core/type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - - -#include -// #include "../config/config.h" - -// #include "../core/hashed_string.hpp" +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 "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE #include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +// #include "../config/config.h" + +// #include "fwd.hpp" @@ -1701,6 +11033,7 @@ template<> struct fnv1a_traits { + using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; @@ -1708,6 +11041,7 @@ template<> struct fnv1a_traits { + using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; @@ -1718,7 +11052,7 @@ /** * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN + * @endcond */ @@ -1735,7 +11069,7 @@ */ template class basic_hashed_string { - using traits_type = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -1744,15 +11078,33 @@ }; // Fowler–Noll–Vo hash function v. 1a - the good - static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT { - return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1); + [[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 = ENTT_ID_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. @@ -1770,8 +11122,8 @@ * @return The numeric representation of the string. */ template - static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT { - return helper(traits_type::offset, str); + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); } /** @@ -1779,20 +11131,8 @@ * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ - static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT { - return helper(traits_type::offset, wrapper.str); - } - - /** - * @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. - */ - static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - ENTT_ID_TYPE partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ @@ -1816,7 +11156,7 @@ */ template constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT - : str{curr}, hash{helper(traits_type::offset, curr)} + : str{curr}, hash{helper(curr)} {} /** @@ -1825,14 +11165,14 @@ * @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(traits_type::offset, wrapper.str)} + : 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. */ - constexpr const value_type * data() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { return str; } @@ -1840,25 +11180,25 @@ * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ - constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; } - - /*! @copydoc value */ - constexpr operator hash_type() 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. */ - constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { return hash == other.hash; } @@ -1879,7 +11219,7 @@ * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -1891,7 +11231,7 @@ * @return True if the two hashed strings are identical, false otherwise. */ template -constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } @@ -1904,7 +11244,7 @@ using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -1912,7 +11252,7 @@ * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -1922,44 +11262,5253 @@ * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } -#endif // ENTT_CORE_HASHED_STRING_HPP - - - -namespace entt { - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. +} + + +} + + +#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_size> - : std::integral_constant -{}; +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. - */ -template -constexpr auto type_list_size_v = type_list_size::value; + * @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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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{}, 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>().get({}))>, type_list<>, type_list>().end())>>...> + >; + using reverse_iterator = iterable_iterator< + typename basic_common_type::reverse_iterator, + type_list_cat_t>().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{}, 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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. */ @@ -2044,225 +16593,303 @@ using type_list_unique_t = typename type_list_unique::type; -/*! @brief Traits class used mainly to push things across boundaries. */ -template -struct named_type_traits; - - -/** - * @brief Specialization used to get rid of constness. - * @tparam Type Named type. - */ -template -struct named_type_traits - : named_type_traits -{}; - - -/** - * @brief Helper type. - * @tparam Type Potentially named type. - */ -template -using named_type_traits_t = typename named_type_traits::type; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - */ -template> -struct is_named_type: std::false_type {}; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - * @tparam Type Potentially named type. - */ -template -struct is_named_type>>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially named type. - */ -template -constexpr auto is_named_type_v = is_named_type::value; - - -/** - * @brief Defines an enum class to use for opaque identifiers and a dedicate - * `to_integer` function to convert the identifiers to their underlying type. - * @param clazz The name to use for the enum class. - * @param type The underlying type for the enum class. - */ -#define ENTT_OPAQUE_TYPE(clazz, type)\ - enum class clazz: type {};\ - constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ - return std::underlying_type_t(id);\ - } - - -} - - -/** - * @brief Utility macro to deal with an issue of MSVC. - * - * See _msvc-doesnt-expand-va-args-correctly_ on SO for all the details. - * - * @param args Argument to expand. - */ -#define ENTT_EXPAND(args) args - - -/** - * @brief Makes an already existing type a named type. - * - * The current definition contains a workaround for Clang 6 because it fails to - * deduce correctly the type to use to specialize the class template.
- * With a compiler that fully supports C++17 and works fine with deduction - * guides, the following should be fine instead: - * - * @code{.cpp} - * std::integral_constant - * @endcode - * - * In order to support even sligthly older compilers, I prefer to stick to the - * implementation below. - * - * @param type Type to assign a name to. - */ -#define ENTT_NAMED_TYPE(type)\ - template<>\ - struct entt::named_type_traits\ - : std::integral_constant>>>{#type}>\ - {\ - static_assert(std::is_same_v, type>);\ - }; - - -/** - * @brief Defines a named type (to use for structs). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_ONLY(clazz, body)\ - struct clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for structs). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { struct clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_STRUCT_OVERLOAD(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for structs). */ -#define ENTT_NAMED_STRUCT(...) ENTT_EXPAND(ENTT_NAMED_STRUCT_OVERLOAD(__VA_ARGS__, ENTT_NAMED_STRUCT_WITH_NAMESPACE, ENTT_NAMED_STRUCT_ONLY,)(__VA_ARGS__)) - - -/** - * @brief Defines a named type (to use for classes). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_ONLY(clazz, body)\ - class clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for classes). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { class clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_CLASS_MACRO(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for classes). */ -#define ENTT_NAMED_CLASS(...) ENTT_EXPAND(ENTT_NAMED_CLASS_MACRO(__VA_ARGS__, ENTT_NAMED_CLASS_WITH_NAMESPACE, ENTT_NAMED_CLASS_ONLY,)(__VA_ARGS__)) - - -#endif // ENTT_CORE_TYPE_TRAITS_HPP - -// #include "../signal/delegate.hpp" -#ifndef ENTT_SIGNAL_DELEGATE_HPP -#define ENTT_SIGNAL_DELEGATE_HPP - - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - - - -namespace entt { +/** + * @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; + + +/*! @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; /** @@ -2274,766 +16901,534 @@ namespace internal { -template -auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); - - -template>> -auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)(); - - -template -struct function_extent; - - -template -struct function_extent { - static constexpr auto value = sizeof...(Args); -}; - - -template -constexpr auto function_extent_v = function_extent::value; - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/*! @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 -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 general purpose invoker with no memory overhead for - * free functions (with or without payload) and members provided along with an - * instance on which to invoke them. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -class delegate { - using proto_fn_type = Ret(const void *, std::tuple); - - template - void connect(std::index_sequence) ENTT_NOEXCEPT { - static_assert(std::is_invocable_r_v>...>); - data = nullptr; - - fn = [](const void *, std::tuple args) -> Ret { - // Ret(...) makes void(...) eat the return values to avoid errors - return Ret(std::invoke(Function, std::forward>>(std::get(args))...)); - }; - } - - template - void connect(Type &value_or_instance, std::index_sequence) ENTT_NOEXCEPT { - static_assert(std::is_invocable_r_v>...>); - data = &value_or_instance; - - fn = [](const void *payload, std::tuple args) -> Ret { - Type *curr = nullptr; - - if constexpr(std::is_const_v) { - curr = static_cast(payload); - } else { - curr = static_cast(const_cast(payload)); - } - - // Ret(...) makes void(...) eat the return values to avoid errors - return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(args))...)); - }; - } - -public: - /*! @brief Function type of the delegate. */ - using function_type = Ret(Args...); - - /*! @brief Default constructor. */ - delegate() ENTT_NOEXCEPT - : fn{nullptr}, data{nullptr} - {} - - /** - * @brief Constructs a delegate and connects a free function to it. - * @tparam Function A valid free function pointer. - */ - template - delegate(connect_arg_t) ENTT_NOEXCEPT - : delegate{} - { - connect(); - } - - /** - * @brief Constructs a delegate and connects a member for a given instance - * or a free function with payload. - * @tparam Candidate Member or free function 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 - delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT - : delegate{} +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(is_iterator_v) { + return true; + } else 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 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{} { - connect(value_or_instance); - } - - /** - * @brief Connects a free function to a delegate. - * @tparam Function A valid free function pointer. - */ - template - void connect() ENTT_NOEXCEPT { - constexpr auto extent = internal::function_extent_v()))>; - connect(std::make_index_sequence{}); - } - - /** - * @brief Connects a member function for a given instance or a free function - * with payload 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 Member or free function 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 { - constexpr auto extent = internal::function_extent_v(), value_or_instance))>; - connect(value_or_instance, std::make_index_sequence{}); - } - - /** - * @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. - */ - 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.
- * An assertion will abort the execution at runtime in debug mode if the - * delegate has not yet been set. - * - * @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(fn); - return fn(data, std::forward_as_tuple(std::forward(args)...)); - } - - /** - * @brief Checks whether a delegate actually stores a listener. - * @return False if the delegate is empty, true otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - // no need to test also data - return fn; - } - - /** - * @brief Compares the contents of two delegates. - * @param other Delegate with which to compare. - * @return False if the two contents differ, true otherwise. - */ - bool operator==(const delegate &other) const ENTT_NOEXCEPT { - return fn == other.fn && data == other.data; - } - -private: - proto_fn_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 -bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Deduction guide. - * - * It allows to deduce the function type of the delegate directly from a - * function provided to the constructor. - * - * @tparam Function A valid free function pointer. - */ -template -delegate(connect_arg_t) ENTT_NOEXCEPT --> delegate>; - - -/** - * @brief Deduction guide. - * - * It allows to deduce the function type of the delegate directly from a member - * or a free function with payload provided to the constructor. - * - * @param value_or_instance A valid reference that fits the purpose. - * @tparam Candidate Member or free function to connect to the delegate. - * @tparam Type Type of class or type of payload. - */ -template -delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT --> delegate>; - - -} - - -#endif // ENTT_SIGNAL_DELEGATE_HPP - -// #include "../signal/sigh.hpp" -#ifndef ENTT_SIGNAL_SIGH_HPP -#define ENTT_SIGNAL_SIGH_HPP - - -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "delegate.hpp" - -// #include "fwd.hpp" -#ifndef ENTT_SIGNAL_FWD_HPP -#define ENTT_SIGNAL_FWD_HPP - - -// #include "../config/config.h" - - - -namespace entt { - - -/*! @class delegate */ -template -class delegate; - -/*! @class sink */ -template -class sink; - -/*! @class sigh */ -template -class sigh; - - -} - - -#endif // ENTT_SIGNAL_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 = typename std::vector>::size_type; - /*! @brief Sink type. */ - using sink_type = entt::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. - */ - 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. - */ - 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 pos = calls.size(); pos; --pos) { - calls[pos-1](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 { - bool stop = false; - - for(auto pos = calls.size(); pos && !stop; --pos) { - if constexpr(std::is_void_v) { - if constexpr(std::is_invocable_r_v) { - calls[pos-1](args...); - stop = func(); - } else { - calls[pos-1](args...); - func(); - } - } else { - if constexpr(std::is_invocable_r_v) { - stop = func(calls[pos-1](args...)); - } else { - func(calls[pos-1](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: - /*! Default constructor. */ - connection() = default; - - /*! @brief Default copy constructor. */ - connection(const connection &) = default; - - /** - * @brief Default move constructor. - * @param other The instance to move from. - */ - connection(connection &&other) - : connection{} - { - std::swap(disconnect, other.disconnect); - std::swap(signal, other.signal); - } - - /*! @brief Default copy assignment operator. @return This connection. */ - connection & operator=(const connection &) = default; - - /** - * @brief Default move assignment operator. - * @param other The instance to move from. - * @return This connection. - */ - connection & operator=(connection &&other) { - if(this != &other) { - auto tmp{std::move(other)}; - disconnect = tmp.disconnect; - signal = tmp.signal; - } - - return *this; - } - - /** - * @brief Checks whether a connection is properly initialized. - * @return True if the connection is properly initialized, false otherwise. - */ - 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: private connection { - /*! Default constructor. */ - scoped_connection() = default; - - /** - * @brief Constructs a scoped connection from a basic connection. - * @param conn A valid connection object. - */ - scoped_connection(const connection &conn) - : connection{conn} - {} - - /*! @brief Default copy constructor, deleted on purpose. */ - scoped_connection(const scoped_connection &) = delete; - - /*! @brief Default move constructor. */ - scoped_connection(scoped_connection &&) = default; - - /*! @brief Automatically breaks the link on destruction. */ - ~scoped_connection() { - connection::release(); - } - - /** - * @brief Default copy assignment operator, deleted on purpose. - * @return This scoped connection. - */ - scoped_connection & operator=(const scoped_connection &) = delete; - - /** - * @brief Default move assignment operator. - * @return This scoped connection. - */ - scoped_connection & operator=(scoped_connection &&) = default; - - /** - * @brief Copies a connection. - * @param other The connection object to copy. - * @return This scoped connection. - */ - scoped_connection & operator=(const connection &other) { - static_cast(*this) = other; - return *this; - } - - /** - * @brief Moves a connection. - * @param other The connection object to move. - * @return This scoped connection. - */ - scoped_connection & operator=(connection &&other) { - static_cast(*this) = std::move(other); - return *this; - } - - using connection::operator bool; - using connection::release; -}; - - -/** - * @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. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -class sink { - using signal_type = sigh; - - 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 - : 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. - */ - bool empty() const ENTT_NOEXCEPT { - return signal->calls.empty(); - } - - /** - * @brief Connects a free function to a signal. - * - * The signal handler performs checks to avoid multiple connections for free - * functions. - * - * @tparam Function A valid free function pointer. - * @return A properly initialized connection object. - */ - template - connection connect() { - disconnect(); - delegate conn{}; - conn.template connect<&release>(); - signal->calls.emplace_back(delegate{connect_arg}); - return { std::move(conn), signal }; - } - - /** - * @brief Connects a member function or a free function with payload 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 delegate. 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 delegate itself. - * - * @tparam Candidate Member or free function to connect to the signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. - * @return A properly initialized connection object. - */ - template - connection connect(Type &value_or_instance) { - disconnect(value_or_instance); - delegate conn{}; - conn.template connect<&sink::release>(value_or_instance); - signal->calls.emplace_back(delegate{connect_arg, value_or_instance}); - return { std::move(conn), signal }; - } - - /** - * @brief Disconnects a free function from a signal. - * @tparam Function A valid free function pointer. - */ - template - void disconnect() { - auto &calls = signal->calls; - delegate delegate{}; - delegate.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); - } - - /** - * @brief Disconnects a member function or a free function with payload from - * a signal. - * @tparam Candidate Member or free function to disconnect from the signal. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. - */ - template - void disconnect(Type &value_or_instance) { - auto &calls = signal->calls; - delegate delegate{}; - delegate.template connect(value_or_instance); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); - } - - /** - * @brief Disconnects member functions or free functions based on an - * instance or specific payload. - * @tparam Type Type of class or type of payload. - * @param value_or_instance A valid reference that fits the purpose. - */ - template - void disconnect(const Type &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: - 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 &) ENTT_NOEXCEPT -> sink; - - -} - - -#endif // ENTT_SIGNAL_SIGH_HPP + 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 @@ -3041,897 +17436,15 @@ #include -#include #include #include #include #include // #include "../config/config.h" +// #include "entity.hpp" + // #include "sparse_set.hpp" -#ifndef ENTT_ENTITY_SPARSE_SET_HPP -#define ENTT_ENTITY_SPARSE_SET_HPP - - -#include -#include -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/algorithm.hpp" - -// #include "entity.hpp" -#ifndef ENTT_ENTITY_ENTITY_HPP -#define ENTT_ENTITY_ENTITY_HPP - - -#include -#include -// #include "../config/config.h" - - - -namespace entt { - - -/** - * @brief Entity traits. - * - * Primary template isn't defined on purpose. All the specializations give a - * compile-time error unless the template parameter is an accepted entity type. - */ -template -struct entt_traits; - - -/** - * @brief Entity traits for a 16 bits entity identifier. - * - * A 16 bits entity identifier guarantees: - * - * * 12 bits for the entity number (up to 4k entities). - * * 4 bit for the version (resets in [0-15]). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint16_t; - /*! @brief Underlying version type. */ - using version_type = std::uint8_t; - /*! @brief Difference type. */ - using difference_type = std::int32_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr std::uint16_t entity_mask = 0xFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr std::uint16_t version_mask = 0xF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto entity_shift = 12; -}; - - -/** - * @brief Entity traits for a 32 bits entity identifier. - * - * A 32 bits entity identifier guarantees: - * - * * 20 bits for the entity number (suitable for almost all the games). - * * 12 bit for the version (resets in [0-4095]). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint32_t; - /*! @brief Underlying version type. */ - using version_type = std::uint16_t; - /*! @brief Difference type. */ - using difference_type = std::int64_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr std::uint32_t entity_mask = 0xFFFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr std::uint32_t version_mask = 0xFFF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto entity_shift = 20; -}; - - -/** - * @brief Entity traits for a 64 bits entity identifier. - * - * A 64 bits entity identifier guarantees: - * - * * 32 bits for the entity number (an indecently large number). - * * 32 bit for the version (an indecently large number). - */ -template<> -struct entt_traits { - /*! @brief Underlying entity type. */ - using entity_type = std::uint64_t; - /*! @brief Underlying version type. */ - using version_type = std::uint32_t; - /*! @brief Difference type. */ - using difference_type = std::int64_t; - - /*! @brief Mask to use to get the entity number out of an identifier. */ - static constexpr std::uint64_t entity_mask = 0xFFFFFFFF; - /*! @brief Mask to use to get the version out of an identifier. */ - static constexpr std::uint64_t version_mask = 0xFFFFFFFF; - /*! @brief Extent of the entity number within an identifier. */ - static constexpr auto entity_shift = 32; -}; - - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -namespace internal { - - -class null { - template - using traits_type = entt_traits>; - -public: - template - constexpr operator Entity() const ENTT_NOEXCEPT { - return Entity{traits_type::entity_mask}; - } - - constexpr bool operator==(null) const ENTT_NOEXCEPT { - return true; - } - - constexpr bool operator!=(null) const ENTT_NOEXCEPT { - return false; - } - - template - constexpr bool operator==(const Entity entity) const ENTT_NOEXCEPT { - return (to_integer(entity) & traits_type::entity_mask) == to_integer(static_cast(*this)); - } - - template - constexpr bool operator!=(const Entity entity) const ENTT_NOEXCEPT { - return !(entity == *this); - } -}; - - -template -constexpr bool operator==(const Entity entity, null other) ENTT_NOEXCEPT { - return other == entity; -} - - -template -constexpr bool operator!=(const Entity entity, null other) ENTT_NOEXCEPT { - return other != entity; -} - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/** - * @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. - */ -constexpr auto null = internal::null{}; - - -} - - -#endif // ENTT_ENTITY_ENTITY_HPP - -// #include "fwd.hpp" -#ifndef ENTT_ENTITY_FWD_HPP -#define ENTT_ENTITY_FWD_HPP - - -#include -// #include "../config/config.h" - -// #include "../core/type_traits.hpp" - - - -namespace entt { - -/*! @class basic_registry */ -template -class basic_registry; - -/*! @class basic_view */ -template -class basic_view; - -/*! @class basic_runtime_view */ -template -class basic_runtime_view; - -/*! @class basic_group */ -template -class basic_group; - -/*! @class basic_observer */ -template -class basic_observer; - -/*! @class basic_actor */ -template -struct basic_actor; - -/*! @class basic_prototype */ -template -class basic_prototype; - -/*! @class basic_snapshot */ -template -class basic_snapshot; - -/*! @class basic_snapshot_loader */ -template -class basic_snapshot_loader; - -/*! @class basic_continuous_loader */ -template -class basic_continuous_loader; - -/*! @brief Alias declaration for the most common use case. */ -ENTT_OPAQUE_TYPE(entity, ENTT_ID_TYPE) - -/*! @brief Alias declaration for the most common use case. */ -ENTT_OPAQUE_TYPE(component, ENTT_ID_TYPE) - -/*! @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 actor = basic_actor; - -/*! @brief Alias declaration for the most common use case. */ -using prototype = basic_prototype; - -/*! @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 Component Types of components iterated by the view. - */ -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 Types Types of components iterated by the group. - */ -template -using group = basic_group; - - -} - - -#endif // ENTT_ENTITY_FWD_HPP - - - -namespace entt { - - -/** - * @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 - * 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. - * - * @note - * Internal data structures arrange elements to maximize performance. Because of - * that, there are no guarantees that elements have the expected order when - * iterate directly the internal packed array (see `data` and `size` member - * functions for that). Use `begin` and `end` instead. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -class sparse_set { - using traits_type = entt_traits>; - - static_assert(ENTT_PAGE_SIZE && ((ENTT_PAGE_SIZE & (ENTT_PAGE_SIZE - 1)) == 0)); - static constexpr auto entt_per_page = ENTT_PAGE_SIZE / sizeof(typename traits_type::entity_type); - - class iterator { - friend class sparse_set; - - using direct_type = const std::vector; - using index_type = typename traits_type::difference_type; - - iterator(direct_type *ref, const index_type idx) ENTT_NOEXCEPT - : direct{ref}, index{idx} - {} - - public: - using difference_type = index_type; - using value_type = Entity; - using pointer = const value_type *; - using reference = const value_type &; - using iterator_category = std::random_access_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return --index, *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - iterator & operator--() ENTT_NOEXCEPT { - return ++index, *this; - } - - iterator operator--(int) ENTT_NOEXCEPT { - iterator orig = *this; - return --(*this), orig; - } - - iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { - index -= value; - return *this; - } - - iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - return iterator{direct, index-value}; - } - - iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { - return other.index - index; - } - - reference operator[](const difference_type value) const ENTT_NOEXCEPT { - const auto pos = size_type(index-value-1); - return (*direct)[pos]; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.index == index; - } - - bool operator!=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - bool operator<(const iterator &other) const ENTT_NOEXCEPT { - return index > other.index; - } - - bool operator>(const iterator &other) const ENTT_NOEXCEPT { - return index < other.index; - } - - bool operator<=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this > other); - } - - bool operator>=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this < other); - } - - pointer operator->() const ENTT_NOEXCEPT { - const auto pos = size_type(index-1); - return &(*direct)[pos]; - } - - reference operator*() const ENTT_NOEXCEPT { - return *operator->(); - } - - private: - direct_type *direct; - index_type index; - }; - - void assure(const std::size_t page) { - if(!(page < reverse.size())) { - reverse.resize(page+1); - } - - if(!reverse[page]) { - reverse[page] = std::make_unique(entt_per_page); - // null is safe in all cases for our purposes - std::fill_n(reverse[page].get(), entt_per_page, null); - } - } - - auto map(const Entity entt) const ENTT_NOEXCEPT { - const auto identifier = to_integer(entt) & traits_type::entity_mask; - const auto page = size_type(identifier / entt_per_page); - const auto offset = size_type(identifier & (entt_per_page - 1)); - return std::make_pair(page, offset); - } - -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_type = iterator; - - /*! @brief Default constructor. */ - sparse_set() = default; - - /** - * @brief Copy constructor. - * @param other The instance to copy from. - */ - sparse_set(const sparse_set &other) - : reverse{}, - direct{other.direct} - { - for(size_type pos{}, last = other.reverse.size(); pos < last; ++pos) { - if(other.reverse[pos]) { - assure(pos); - std::copy_n(other.reverse[pos].get(), entt_per_page, reverse[pos].get()); - } - } - } - - /*! @brief Default move constructor. */ - sparse_set(sparse_set &&) = default; - - /*! @brief Default destructor. */ - virtual ~sparse_set() ENTT_NOEXCEPT = default; - - /** - * @brief Copy assignment operator. - * @param other The instance to copy from. - * @return This sparse set. - */ - sparse_set & operator=(const sparse_set &other) { - if(&other != this) { - auto tmp{other}; - *this = std::move(tmp); - } - - return *this; - } - - /*! @brief Default move assignment operator. @return This sparse set. */ - sparse_set & operator=(sparse_set &&) = default; - - /** - * @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) { - direct.reserve(cap); - } - - /** - * @brief Returns the number of elements that a sparse set has currently - * allocated space for. - * @return Capacity of the sparse set. - */ - size_type capacity() const ENTT_NOEXCEPT { - return direct.capacity(); - } - - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - // conservative approach - if(direct.empty()) { - reverse.clear(); - } - - reverse.shrink_to_fit(); - direct.shrink_to_fit(); - } - - /** - * @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. - */ - size_type extent() const ENTT_NOEXCEPT { - return reverse.size() * entt_per_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. - */ - size_type size() const ENTT_NOEXCEPT { - return direct.size(); - } - - /** - * @brief Checks whether a sparse set is empty. - * @return True if the sparse set is empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return direct.empty(); - } - - /** - * @brief Direct access to the internal packed array. - * - * The returned pointer is such that range `[data(), data() + size()]` is - * always a valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order, even though `respect` has been - * previously invoked. Internal data structures arrange elements to maximize - * performance. Accessing them directly gives a performance boost but less - * guarantees. Use `begin` and `end` if you want to iterate the sparse set - * in the expected order. - * - * @return A pointer to the internal packed array. - */ - const entity_type * data() const ENTT_NOEXCEPT { - return direct.data(); - } - - /** - * @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()`. - * - * @note - * Random access iterators stay true to the order imposed by a call to - * `respect`. - * - * @return An iterator to the first entity of the internal packed array. - */ - iterator_type begin() const ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = direct.size(); - return iterator_type{&direct, pos}; - } - - /** - * @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. - * - * @note - * Random access iterators stay true to the order imposed by a call to - * `respect`. - * - * @return An iterator to the element following the last entity of the - * internal packed array. - */ - iterator_type end() const ENTT_NOEXCEPT { - return iterator_type{&direct, {}}; - } - - /** - * @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. - */ - iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - return has(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. - */ - bool has(const entity_type entt) const ENTT_NOEXCEPT { - auto [page, offset] = map(entt); - // testing against null permits to avoid accessing the direct vector - return (page < reverse.size() && reverse[page] && reverse[page][offset] != 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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - * @return The position of the entity in the sparse set. - */ - size_type index(const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(has(entt)); - auto [page, offset] = map(entt); - return size_type(reverse[page][offset]); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @param entt A valid entity identifier. - */ - void construct(const entity_type entt) { - ENTT_ASSERT(!has(entt)); - auto [page, offset] = map(entt); - assure(page); - reverse[page][offset] = entity_type(direct.size()); - direct.push_back(entt); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set already contains the given entity. - * - * @tparam It Type of forward 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 batch(It first, It last) { - std::for_each(std::make_reverse_iterator(last), std::make_reverse_iterator(first), [this, next = direct.size()](const auto entt) mutable { - ENTT_ASSERT(!has(entt)); - auto [page, offset] = map(entt); - assure(page); - reverse[page][offset] = entity_type(next++); - }); - - direct.insert(direct.end(), first, last); - } - - /** - * @brief Removes an entity from a sparse set. - * - * @warning - * Attempting to remove an entity that doesn't belong to the sparse set - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entity. - * - * @param entt A valid entity identifier. - */ - void destroy(const entity_type entt) { - ENTT_ASSERT(has(entt)); - auto [from_page, from_offset] = map(entt); - auto [to_page, to_offset] = map(direct.back()); - direct[size_type(reverse[from_page][from_offset])] = entity_type(direct.back()); - reverse[to_page][to_offset] = reverse[from_page][from_offset]; - reverse[from_page][from_offset] = null; - direct.pop_back(); - } - - /** - * @brief Swaps two entities in the internal packed array. - * - * 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.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entities. - * - * @param lhs A valid position within the sparse set. - * @param rhs A valid position within the sparse set. - */ - virtual void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT { - ENTT_ASSERT(lhs < direct.size()); - ENTT_ASSERT(rhs < direct.size()); - auto [src_page, src_offset] = map(direct[lhs]); - auto [dst_page, dst_offset] = map(direct[rhs]); - std::swap(reverse[src_page][src_offset], reverse[dst_page][dst_offset]); - std::swap(direct[lhs], direct[rhs]); - } - - /** - * @brief Sort elements according to the given comparison function. - * - * Sort the elements so that iterating the range with a couple of iterators - * returns them 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 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 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 function 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. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `sort` has been invoked. - * - * @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 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 algo A valid sort function object. - * @param args Arguments to forward to the sort function object, if any. - */ - template - void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(first > last)); - - std::vector copy(last - first); - const auto offset = std::distance(last, end()); - std::iota(copy.begin(), copy.end(), size_type{}); - - algo(copy.rbegin(), copy.rend(), [this, offset, compare = std::move(compare)](const auto lhs, const auto rhs) { - return compare(std::as_const(direct[lhs+offset]), std::as_const(direct[rhs+offset])); - }, std::forward(args)...); - - for(size_type pos{}, length = copy.size(); pos < length; ++pos) { - auto curr = pos; - auto next = copy[curr]; - - while(curr != next) { - swap(copy[curr] + offset, copy[next] + offset); - copy[curr] = curr; - curr = next; - next = copy[curr]; - } - } - } - - /** - * @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. - * - * @note - * Attempting to iterate elements using the raw pointer returned by `data` - * gives no guarantees on the order, even though `respect` has been invoked. - * - * @param other The sparse sets that imposes the order of the entities. - */ - void respect(const sparse_set &other) ENTT_NOEXCEPT { - const auto to = other.end(); - auto from = other.begin(); - - size_type pos = direct.size() - 1; - - while(pos && from != to) { - if(has(*from)) { - if(*from != direct[pos]) { - swap(pos, index(*from)); - } - - --pos; - } - - ++from; - } - } - - /** - * @brief Resets a sparse set. - */ - void reset() { - reverse.clear(); - direct.clear(); - } - -private: - std::vector> reverse; - std::vector direct; -}; - - -} - - -#endif // ENTT_ENTITY_SPARSE_SET_HPP - -// #include "entity.hpp" // #include "fwd.hpp" @@ -3973,120 +17486,130 @@ * have a valid reference and won't be updated accordingly). * * @warning - * Lifetime of a view must overcome the one of the registry that generated it. + * 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 { - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; - - using underlying_iterator_type = typename sparse_set::iterator_type; - using traits_type = entt_traits>; - - class iterator { - friend class basic_runtime_view; - - iterator(underlying_iterator_type first, underlying_iterator_type last, const sparse_set * const *others, const sparse_set * const *length) ENTT_NOEXCEPT - : begin{first}, - end{last}, - from{others}, - to{length} - { - if(begin != end && !valid()) { - ++(*this); - } - } - - bool valid() const ENTT_NOEXCEPT { - return std::all_of(from, to, [entt = *begin](const auto *view) { - return view->has(entt); - }); +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_type::difference_type; - using value_type = typename underlying_iterator_type::value_type; - using pointer = typename underlying_iterator_type::pointer; - using reference = typename underlying_iterator_type::reference; - using iterator_category = std::forward_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return (++begin != end && !valid()) ? ++(*this) : *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; + 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; } - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.begin == begin; - } - - bool operator!=(const iterator &other) const ENTT_NOEXCEPT { + 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); } - pointer operator->() const ENTT_NOEXCEPT { - return begin.operator->(); - } - - reference operator*() const ENTT_NOEXCEPT { + [[nodiscard]] pointer operator->() const { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const { return *operator->(); } private: - underlying_iterator_type begin; - underlying_iterator_type end; - const sparse_set * const *from; - const sparse_set * const *to; - }; - - basic_runtime_view(std::vector *> others) ENTT_NOEXCEPT - : pools{std::move(others)} - { - const auto it = std::min_element(pools.begin(), pools.end(), [](const auto *lhs, const auto *rhs) { - return (!lhs && rhs) || (lhs && rhs && lhs->size() < rhs->size()); - }); - - // brings the best candidate (if any) on front of the vector - std::rotate(pools.begin(), it, pools.end()); - } - - bool valid() const ENTT_NOEXCEPT { + 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 = typename sparse_set::entity_type; + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using size_type = typename sparse_set::size_type; - /*! @brief Input iterator type. */ - using iterator_type = iterator; - - /** - * @brief Estimates the number of entities that have the given components. - * @return Estimated number of entities that have the given components. - */ - size_type size() const ENTT_NOEXCEPT { + 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 Checks if the view is definitely empty. - * @return True if the view is definitely empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return !valid() || pools.front()->empty(); - } - - /** * @brief Returns an iterator to the first entity that has the given * components. * @@ -4094,22 +17617,10 @@ * components. If the view is empty, the returned iterator will be equal to * `end()`. * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the first entity that has the given components. */ - iterator_type begin() const ENTT_NOEXCEPT { - iterator_type it{}; - - if(valid()) { - const auto &pool = *pools.front(); - const auto * const *data = pools.data(); - it = { pool.begin(), pool.end(), data + 1, data + pools.size() }; - } - - return it; + [[nodiscard]] iterator begin() const { + return valid() ? iterator{pools, filter, pools[0]->begin()} : iterator{}; } /** @@ -4120,22 +17631,11 @@ * has the given components. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * * @return An iterator to the entity following the last entity that has the * given components. */ - iterator_type end() const ENTT_NOEXCEPT { - iterator_type it{}; - - if(valid()) { - const auto &pool = *pools.front(); - it = { pool.end(), pool.end(), nullptr, nullptr }; - } - - return it; + [[nodiscard]] iterator end() const { + return valid() ? iterator{pools, filter, pools[0]->end()} : iterator{}; } /** @@ -4143,10 +17643,9 @@ * @param entt A valid entity identifier. * @return True if the view contains the given entity, false otherwise. */ - bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return valid() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *view) { - return view->find(entt) != view->end(); - }); + [[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); }); } /** @@ -4166,38 +17665,6884 @@ */ template void each(Func func) const { - std::for_each(begin(), end(), func); - } - -private: - std::vector *> pools; -}; - - -} - - -#endif // ENTT_ENTITY_RUNTIME_VIEW_HPP + 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 "snapshot.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{}, 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{}, 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 ® +}; + + +/** + * @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 ® +}; + + +/** + * @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 ®, 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 ®, 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 ®, 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.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.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 ®, 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.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.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 ®, basic_observer &obs) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(basic_registry ®, 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 ®, 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 ®, 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 ® +}; + + +/** + * @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 ® +}; + + +/** + * @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 ®, 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 ®, 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 ®) { + 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 ®, 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 ®) 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 ®) { + 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 ®) { 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 ®) { + 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 ®) { + 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 "../config/config.h" +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" // #include "entity.hpp" // #include "fwd.hpp" +// #include "registry.hpp" + namespace entt { @@ -4215,49 +24560,47 @@ */ template class basic_snapshot { - /*! @brief A registry is allowed to create snapshots. */ - friend class basic_registry; - - using follow_fn_type = Entity(const basic_registry &, const Entity); - using traits_type = entt_traits>; - - basic_snapshot(const basic_registry *source, Entity init, follow_fn_type *fn) ENTT_NOEXCEPT - : reg{source}, - seed{init}, - follow{fn} - {} + 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 has(entt)) { - if constexpr(std::is_empty_v) { - archive(entt); - } else { - archive(entt, reg->template get(entt)); - } - } - } - } - - template - void component(Archive &archive, It first, It last, std::index_sequence) const { - std::array size{}; + 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 has(entt) ? ++size[Indexes] : size[Indexes]), ...); - } - - (get(archive, size[Indexes], first, last), ...); - } - -public: + ((reg->template all_of(entt) ? ++size[Index] : 0u), ...); + } + + (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; @@ -4265,10 +24608,10 @@ basic_snapshot & operator=(basic_snapshot &&) = default; /** - * @brief Puts aside all the entities that are still in use. + * @brief Puts aside all the entities from the underlying registry. * * Entities are serialized along with their versions. Destroyed entities are - * not taken in consideration by this function. + * taken in consideration as well by this function. * * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. @@ -4276,35 +24619,15 @@ */ template const basic_snapshot & entities(Archive &archive) const { - archive(typename traits_type::entity_type(reg->alive())); - reg->each([&archive](const auto entt) { archive(entt); }); - return *this; - } - - /** - * @brief Puts aside destroyed entities. - * - * Entities are serialized along with their versions. Entities that are - * still in use are not taken in consideration 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 & destroyed(Archive &archive) const { - auto size = reg->size() - reg->alive(); - archive(typename traits_type::entity_type(size)); - - if(size) { - auto curr = seed; - archive(curr); - - for(--size; size; --size) { - curr = follow(*reg, curr); - archive(curr); - } - } + 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; } @@ -4322,26 +24645,14 @@ */ template const basic_snapshot & component(Archive &archive) const { - if constexpr(sizeof...(Component) == 1) { - const auto sz = reg->template size(); - const auto *entities = reg->template data(); - - archive(typename traits_type::entity_type(sz)); - - for(std::remove_const_t pos{}; pos < sz; ++pos) { - const auto entt = entities[pos]; - - if constexpr(std::is_empty_v) { - archive(entt); - } else { - archive(entt, reg->template get(entt)); - } - }; + 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; + return *this; + } } /** @@ -4360,14 +24671,12 @@ */ template const basic_snapshot & component(Archive &archive, It first, It last) const { - component(archive, first, last, std::make_index_sequence{}); - return *this; - } - -private: - const basic_registry *reg; - const Entity seed; - follow_fn_type *follow; + component(archive, first, last, std::index_sequence_for{}); + return *this; + } + +private: + const basic_registry *reg; }; @@ -4383,55 +24692,49 @@ */ template class basic_snapshot_loader { - /*! @brief A registry is allowed to create snapshot loaders. */ - friend class basic_registry; - - using force_fn_type = void(basic_registry &, const Entity, const bool); - using traits_type = entt_traits>; - - basic_snapshot_loader(basic_registry *source, force_fn_type *fn) ENTT_NOEXCEPT - : reg{source}, - force{fn} - { - // to restore a snapshot as a whole requires a clean registry - ENTT_ASSERT(reg->empty()); - } - - template - void assure(Archive &archive, bool discard) const { + using traits_type = entt_traits; + + template + void assign(Archive &archive) const { typename traits_type::entity_type length{}; archive(length); - while(length--) { - Entity entt{}; - archive(entt); - force(*reg, entt, discard); - } - } - - template - void assign(Archive &archive, Args... args) const { - typename traits_type::entity_type length{}; - archive(length); - - while(length--) { - static constexpr auto discard = false; - Entity entt{}; - - if constexpr(std::is_empty_v) { + entity_type entt{}; + + if constexpr(std::tuple_size_vtemplate view().get({}))> == 0) { + while(length--) { archive(entt); - force(*reg, entt, discard); - reg->template assign(args..., entt); - } else { - Type instance{}; + 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); - force(*reg, entt, discard); - reg->template assign(args..., entt, std::as_const(instance)); - } - } - } - -public: + 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; @@ -4450,25 +24753,20 @@ */ template const basic_snapshot_loader & entities(Archive &archive) const { - static constexpr auto discard = false; - assure(archive, discard); - return *this; - } - - /** - * @brief Restores entities that were destroyed during serialization. - * - * This function restores the entities that were destroyed 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 & destroyed(Archive &archive) const { - static constexpr auto discard = true; - assure(archive, discard); + 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; } @@ -4503,15 +24801,14 @@ */ const basic_snapshot_loader & orphans() const { reg->orphans([this](const auto entt) { - reg->destroy(entt); + reg->release(entt); }); return *this; } private: - basic_registry *reg; - force_fn_type *force; + basic_registry *reg; }; @@ -4519,7 +24816,7 @@ * @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 accomodate in a registry + * (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. @@ -4533,12 +24830,10 @@ */ template class basic_continuous_loader { - using traits_type = entt_traits>; + using traits_type = entt_traits; void destroy(Entity entt) { - const auto it = remloc.find(entt); - - if(it == remloc.cend()) { + 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); @@ -4552,45 +24847,68 @@ const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); } else { - remloc[entt].first = reg->valid(remloc[entt].first) ? remloc[entt].first : reg->create(); + 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(Other &instance, Member Type:: *member) { + 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) { + } else if constexpr(std::is_same_v) { instance.*member = map(instance.*member); } else { // maybe a container? let's try... - for(auto &entt: instance.*member) { - entt = map(entt); - } - } - } - - template - void assure(Archive &archive, void(basic_continuous_loader:: *member)(Entity)) { - typename traits_type::entity_type length{}; - archive(length); - - while(length--) { - Entity entt{}; - archive(entt); - (this->*member)(entt); + update(0, instance.*member); } } template - void reset() { + void remove_if_exists() { for(auto &&ref: remloc) { const auto local = ref.second.first; if(reg->valid(local)) { - reg->template reset(local); + reg->template remove(local); } } } @@ -4600,19 +24918,22 @@ typename traits_type::entity_type length{}; archive(length); - while(length--) { - Entity entt{}; - - if constexpr(std::is_empty_v) { + entity_type entt{}; + + if constexpr(std::tuple_size_vtemplate view().get({}))> == 0) { + while(length--) { archive(entt); restore(entt); - reg->template assign_or_replace(map(entt)); - } else { - Other instance{}; + reg->template emplace_or_replace(map(entt)); + } + } else { + Other instance{}; + + while(length--) { archive(entt, instance); (update(instance, member), ...); restore(entt); - reg->template assign_or_replace(map(entt), std::as_const(instance)); + reg->template emplace_or_replace(map(entt), std::move(instance)); } } } @@ -4622,7 +24943,7 @@ using entity_type = Entity; /** - * @brief Constructs a loader that is bound to a given registry. + * @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 @@ -4647,23 +24968,24 @@ */ template basic_continuous_loader & entities(Archive &archive) { - assure(archive, &basic_continuous_loader::restore); - return *this; - } - - /** - * @brief Restores entities that were destroyed during serialization. - * - * This function restores the entities that were destroyed 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 & destroyed(Archive &archive) { - assure(archive, &basic_continuous_loader::destroy); + 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; } @@ -4688,7 +25010,7 @@ */ template basic_continuous_loader & component(Archive &archive, Member Type:: *... member) { - (reset(), ...); + (remove_if_exists(), ...); (assign(archive, member...), ...); return *this; } @@ -4735,7 +25057,7 @@ */ basic_continuous_loader & orphans() { reg->orphans([this](const auto entt) { - reg->destroy(entt); + reg->release(entt); }); return *this; @@ -4746,7 +25068,7 @@ * @param entt An entity identifier. * @return True if `entity` is managed by the loader, false otherwise. */ - bool has(entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] bool contains(entity_type entt) const ENTT_NOEXCEPT { return (remloc.find(entt) != remloc.cend()); } @@ -4755,7 +25077,7 @@ * @param entt An entity identifier. * @return The local identifier if any, the null entity otherwise. */ - entity_type map(entity_type entt) const ENTT_NOEXCEPT { + [[nodiscard]] entity_type map(entity_type entt) const ENTT_NOEXCEPT { const auto it = remloc.find(entt); entity_type other = null; @@ -4775,28 +25097,914 @@ } -#endif // ENTT_ENTITY_SNAPSHOT_HPP - -// #include "storage.hpp" -#ifndef ENTT_ENTITY_STORAGE_HPP -#define ENTT_ENTITY_STORAGE_HPP - - -#include +#endif + +// #include "entity/sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + + +#include #include -#include -#include -#include -#include -#include +#include +#include +#include // #include "../config/config.h" // #include "../core/algorithm.hpp" -// #include "sparse_set.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 static_cast(traits_type::to_entity(entt) / sparse_page); + } + + [[nodiscard]] static auto offset(const Entity entt) ENTT_NOEXCEPT { + return static_cast(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 = static_cast(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 = static_cast(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 : static_cast(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 static_cast(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 = static_cast(traits_type::to_entity(free_list)); + assure_page(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 = static_cast(traits_type::to_entity(entt)); + const auto to = static_cast(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 { @@ -4811,144 +26019,332 @@ * to the entities. * * @note - * Entities and objects have the same order. It's guaranteed both in case of raw - * access (either to entities or objects) and when using random or input access - * iterators. + * Entities and objects have the same order. * * @note - * Internal data structures arrange elements to maximize performance. Because of - * that, there are no guarantees that elements have the expected order when - * iterate directly the internal packed array (see `raw` and `size` member - * functions for that). Use `begin` and `end` instead. + * 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. Temporary objects are returned in - * place of the instances of the components and raw access isn't available for - * them. + * 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. - */ -template> -class basic_storage: public sparse_set { - using underlying_type = sparse_set; - using traits_type = entt_traits>; - - template - class iterator { - friend class basic_storage; - - using instance_type = std::conditional_t, std::vector>; - using index_type = typename traits_type::difference_type; - - iterator(instance_type *ref, const index_type idx) ENTT_NOEXCEPT - : instances{ref}, index{idx} - {} - - public: - using difference_type = index_type; - using value_type = Type; - using pointer = std::conditional_t; - using reference = std::conditional_t; + * @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; - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { + 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; } - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; return ++(*this), orig; } - iterator & operator--() ENTT_NOEXCEPT { + storage_iterator & operator--() ENTT_NOEXCEPT { return ++index, *this; } - iterator operator--(int) ENTT_NOEXCEPT { - iterator orig = *this; - return --(*this), orig; - } - - iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { + 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; } - iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - return iterator{instances, index-value}; - } - - iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { + 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); } - iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { return (*this + -value); } - difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { + difference_type operator-(const storage_iterator &other) const ENTT_NOEXCEPT { return other.index - index; } - reference operator[](const difference_type value) const ENTT_NOEXCEPT { + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { const auto pos = size_type(index-value-1); - return (*instances)[pos]; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { + return (*packed)[page(pos)][offset(pos)]; + } + + [[nodiscard]] bool operator==(const storage_iterator &other) const ENTT_NOEXCEPT { return other.index == index; } - bool operator!=(const iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator!=(const storage_iterator &other) const ENTT_NOEXCEPT { return !(*this == other); } - bool operator<(const iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator<(const storage_iterator &other) const ENTT_NOEXCEPT { return index > other.index; } - bool operator>(const iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator>(const storage_iterator &other) const ENTT_NOEXCEPT { return index < other.index; } - bool operator<=(const iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator<=(const storage_iterator &other) const ENTT_NOEXCEPT { return !(*this > other); } - bool operator>=(const iterator &other) const ENTT_NOEXCEPT { + [[nodiscard]] bool operator>=(const storage_iterator &other) const ENTT_NOEXCEPT { return !(*this < other); } - pointer operator->() const ENTT_NOEXCEPT { - const auto pos = size_type(index-1); - return &(*instances)[pos]; - } - - reference operator*() const ENTT_NOEXCEPT { + [[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: - instance_type *instances; - index_type index; - }; - -public: - /*! @brief Type of the objects associated with the entities. */ - using object_type = Type; + 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 = typename underlying_type::entity_type; + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using size_type = typename underlying_type::size_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_type = iterator; + using iterator = storage_iterator; /*! @brief Constant random access iterator type. */ - using const_iterator_type = iterator; + 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. @@ -4960,128 +26356,153 @@ */ void reserve(const size_type cap) { underlying_type::reserve(cap); - instances.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(); - instances.shrink_to_fit(); + release_unused_pages(); } /** * @brief Direct access to the array of objects. - * - * The returned pointer is such that range `[raw(), raw() + size()]` is - * always a valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order, even though either `sort` or - * `respect` has been previously invoked. Internal data structures arrange - * elements to maximize performance. Accessing them directly gives a - * performance boost but less guarantees. Use `begin` and `end` if you want - * to iterate the storage in the expected order. - * * @return A pointer to the array of objects. */ - const object_type * raw() const ENTT_NOEXCEPT { - return instances.data(); + [[nodiscard]] const_pointer raw() const ENTT_NOEXCEPT { + return packed; } /*! @copydoc raw */ - object_type * raw() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).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 given type. If - * the storage is empty, the returned iterator will be equal to `end()`. - * - * @note - * Random access iterators stay true to the order imposed by a call to - * either `sort` or `respect`. - * - * @return An iterator to the first instance of the given type. - */ - const_iterator_type cbegin() const ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = underlying_type::size(); - return const_iterator_type{&instances, pos}; + * 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 */ - const_iterator_type begin() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { return cbegin(); } /*! @copydoc begin */ - iterator_type begin() ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = underlying_type::size(); - return iterator_type{&instances, pos}; + [[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 given type. Attempting to dereference the returned iterator + * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * - * @note - * Random access iterators stay true to the order imposed by a call to - * either `sort` or `respect`. - * * @return An iterator to the element following the last instance of the - * given type. - */ - const_iterator_type cend() const ENTT_NOEXCEPT { - return const_iterator_type{&instances, {}}; + * internal array. + */ + [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + return const_iterator{std::addressof(packed), {}}; } /*! @copydoc cend */ - const_iterator_type end() const ENTT_NOEXCEPT { + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { return cend(); } /*! @copydoc end */ - iterator_type end() ENTT_NOEXCEPT { - return iterator_type{&instances, {}}; - } - - /** - * @brief Returns the object associated with an entity. + [[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.
- * An assertion will abort the execution at runtime in debug mode if the - * storage doesn't contain the given entity. - * - * @param entt A valid entity identifier. - * @return The object associated with the entity. - */ - const object_type & get(const entity_type entt) const ENTT_NOEXCEPT { - return instances[underlying_type::index(entt)]; + * 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 */ - object_type & get(const entity_type entt) ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).get(entt)); - } - - /** - * @brief Returns a pointer to the object associated with an entity, if any. - * @param entt A valid entity identifier. - * @return The object associated with the entity, if any. - */ - const object_type * try_get(const entity_type entt) const ENTT_NOEXCEPT { - return underlying_type::has(entt) ? (instances.data() + underlying_type::index(entt)) : nullptr; - } - - /*! @copydoc try_get */ - object_type * try_get(const entity_type entt) ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).try_get(entt)); + [[nodiscard]] value_type & get(const entity_type entt) ENTT_NOEXCEPT { + return const_cast(std::as_const(*this).get(entt)); } /** @@ -5093,121 +26514,110 @@ * * @warning * Attempting to use an entity that already belongs to the storage results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. + * 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 The object associated with the entity. + * @return A reference to the newly created object. */ template - object_type & construct(const entity_type entt, Args &&... args) { - if constexpr(std::is_aggregate_v) { - instances.emplace_back(Type{std::forward(args)...}); - } else { - instances.emplace_back(std::forward(args)...); - } - - // entity goes after component in case constructor throws - underlying_type::construct(entt); - return instances.back(); - } - - /** - * @brief Assigns one or more entities to a storage and default constructs - * their objects. - * - * The object type must be at least move and default insertable. + 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.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. - * - * @tparam It Type of forward iterator. + * 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. - * @return An iterator to the list of instances just created and sorted the - * same of the entities. - */ - template - iterator_type batch(It first, It last) { - instances.resize(instances.size() + std::distance(first, last)); - // entity goes after component in case constructor throws - underlying_type::batch(first, last); - return begin(); - } - - /** - * @brief Assigns one or more entities to a storage and copy constructs - * their objects. - * - * The object type must be at least move and copy insertable. - * - * @sa batch - * - * @tparam It Type of forward 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 The value to initialize the new objects with. - * @return An iterator to the list of instances just created and sorted the - * same of the entities. + * @param value An instance of the object to construct. */ template - iterator_type batch(It first, It last, const object_type &value) { - instances.resize(instances.size() + std::distance(first, last), value); - // entity goes after component in case constructor throws - underlying_type::batch(first, last); - return begin(); - } - - /** - * @brief Removes an entity from a storage and destroys its object. - * - * @warning - * Attempting to use an entity that doesn't belong to the storage results in - * undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage doesn't contain the given entity. - * - * @param entt A valid entity identifier. - */ - void destroy(const entity_type entt) { - auto other = std::move(instances.back()); - instances[underlying_type::index(entt)] = std::move(other); - instances.pop_back(); - underlying_type::destroy(entt); - } - - /** - * @brief Swaps entities and objects in the internal packed arrays. - * - * @warning - * Attempting to swap entities that don't belong to the sparse set results - * in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * sparse set doesn't contain the given entities. - * - * @param lhs A valid position within the sparse set. - * @param rhs A valid position within the sparse set. - */ - void swap(const size_type lhs, const size_type rhs) ENTT_NOEXCEPT override { - ENTT_ASSERT(lhs < instances.size()); - ENTT_ASSERT(rhs < instances.size()); - std::swap(instances[lhs], instances[rhs]); - underlying_type::swap(lhs, rhs); + 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. * - * Sort the elements so that iterating the range with a couple of iterators - * returns them 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: @@ -5227,267 +26637,402 @@ * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * - * The comparison function 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. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `sort` has been invoked. + * @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 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 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(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(first > last)); - - const auto from = underlying_type::begin() + std::distance(begin(), first); - const auto to = from + std::distance(first, last); - - if constexpr(std::is_invocable_v) { - static_assert(!std::is_empty_v); - - underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { - return compare(std::as_const(instances[underlying_type::index(lhs)]), std::as_const(instances[underlying_type::index(rhs)])); - }, std::move(algo), std::forward(args)...); - } else { - underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward(args)...); - } - } - - /*! @brief Resets a storage. */ - void reset() { - underlying_type::reset(); - instances.clear(); - } - -private: - std::vector instances; -}; - - -/*! @copydoc basic_storage */ -template -class basic_storage>>: public sparse_set { - using traits_type = entt_traits>; - using underlying_type = sparse_set; - - class iterator { - friend class basic_storage; - - using index_type = typename traits_type::difference_type; - - iterator(const index_type idx) ENTT_NOEXCEPT - : index{idx} - {} - - public: - using difference_type = index_type; - using value_type = Type; - using pointer = const value_type *; - using reference = value_type; - using iterator_category = std::input_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return --index, *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - iterator & operator--() ENTT_NOEXCEPT { - return ++index, *this; - } - - iterator operator--(int) ENTT_NOEXCEPT { - iterator orig = *this; - return --(*this), orig; - } - - iterator & operator+=(const difference_type value) ENTT_NOEXCEPT { - index -= value; - return *this; - } - - iterator operator+(const difference_type value) const ENTT_NOEXCEPT { - return iterator{index-value}; - } - - iterator & operator-=(const difference_type value) ENTT_NOEXCEPT { - return (*this += -value); - } - - iterator operator-(const difference_type value) const ENTT_NOEXCEPT { - return (*this + -value); - } - - difference_type operator-(const iterator &other) const ENTT_NOEXCEPT { - return other.index - index; - } - - reference operator[](const difference_type) const ENTT_NOEXCEPT { - return {}; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.index == index; - } - - bool operator!=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - bool operator<(const iterator &other) const ENTT_NOEXCEPT { - return index > other.index; - } - - bool operator>(const iterator &other) const ENTT_NOEXCEPT { - return index < other.index; - } - - bool operator<=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this > other); - } - - bool operator>=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this < other); - } - - pointer operator->() const ENTT_NOEXCEPT { - return nullptr; - } - - reference operator*() const ENTT_NOEXCEPT { - return {}; - } - - private: - index_type index; - }; - -public: - /*! @brief Type of the objects associated with the entities. */ - using object_type = Type; + 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 = typename underlying_type::entity_type; + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using size_type = typename underlying_type::size_type; - /*! @brief Random access iterator type. */ - using iterator_type = iterator; - - /** - * @brief Returns an iterator to the beginning. - * - * The returned iterator points to the first instance of the given type. If - * the storage is empty, the returned iterator will be equal to `end()`. - * - * @note - * Input iterators stay true to the order imposed by a call to either `sort` - * or `respect`. - * - * @return An iterator to the first instance of the given type. - */ - iterator_type cbegin() const ENTT_NOEXCEPT { - const typename traits_type::difference_type pos = underlying_type::size(); - return iterator_type{pos}; - } - - /*! @copydoc cbegin */ - iterator_type begin() const ENTT_NOEXCEPT { - return cbegin(); - } - - /** - * @brief Returns an iterator to the end. - * - * The returned iterator points to the element following the last instance - * of the given type. Attempting to dereference the returned iterator - * results in undefined behavior. - * - * @note - * Input iterators stay true to the order imposed by a call to either `sort` - * or `respect`. - * - * @return An iterator to the element following the last instance of the - * given type. - */ - iterator_type cend() const ENTT_NOEXCEPT { - return iterator_type{}; - } - - /*! @copydoc cend */ - iterator_type end() const ENTT_NOEXCEPT { - return cend(); - } - - /** - * @brief Returns the object associated with an entity. - * - * @note - * Empty types aren't explicitly instantiated. Therefore, this function - * always returns a temporary object. + 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.
- * An assertion will abort the execution at runtime in debug mode if the - * storage doesn't contain the given entity. - * - * @param entt A valid entity identifier. - * @return The object associated with the entity. - */ - object_type get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(underlying_type::has(entt)); - return {}; + * 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. * - * The object type must be at least default constructible. - * * @warning * Attempting to assign an entity that already belongs to the storage - * results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * storage already contains the given entity. - * - * @tparam It Type of forward iterator. + * 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. - * @return An iterator to the list of instances just created and sorted the - * same of the entities. */ template - iterator_type batch(It first, It last, const object_type & = {}) { - underlying_type::batch(first, last); - return begin(); - } -}; - -/*! @copydoc basic_storage */ -template -struct storage: basic_storage {}; - - -} - - -#endif // ENTT_ENTITY_STORAGE_HPP - -// #include "utility.hpp" + 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 @@ -5512,7 +27057,7 @@ * @tparam Type List of types. */ template -constexpr exclude_t exclude{}; +inline constexpr exclude_t exclude{}; /** @@ -5528,886 +27073,15 @@ * @tparam Type List of types. */ template -constexpr get_t get{}; - - -} - - -#endif // ENTT_ENTITY_UTILITY_HPP - -// #include "entity.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 "sparse_set.hpp" - -// #include "storage.hpp" - -// #include "utility.hpp" - -// #include "fwd.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 the 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.
- * In general, non-owning groups don't stay true to the order of any set of - * components unless users explicitly sort 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 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 - * 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 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 set of entities). - * - * @warning - * Lifetime of a group must overcome the one 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 component observed by the group. - * @tparam Other Other types of components observed by the group. - */ -template -class basic_group, get_t> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; - - template - using pool_type = std::conditional_t, const storage>, storage>; - - // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug) - basic_group(sparse_set *ref, storage> *get, storage> *... other) ENTT_NOEXCEPT - : handler{ref}, - pools{get, other...} - {} - - template - void traverse(Func func, type_list) const { - for(const auto entt: *handler) { - if constexpr(std::is_invocable_v({}))...>) { - func(std::get *>(pools)->get(entt)...); - } else { - func(entt, std::get *>(pools)->get(entt)...); - } - }; - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = typename sparse_set::entity_type; - /*! @brief Unsigned integer type. */ - using size_type = typename sparse_set::size_type; - /*! @brief Input iterator type. */ - using iterator_type = typename sparse_set::iterator_type; - - /** - * @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 - size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); - } - - /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. - */ - size_type size() const ENTT_NOEXCEPT { - return handler->size(); - } - - /** - * @brief Returns the number of elements that a group has currently - * allocated space for. - * @return Capacity of the group. - */ - size_type capacity() const ENTT_NOEXCEPT { - return handler->capacity(); - } - - /*! @brief Requests the removal of unused capacity. */ - void shrink_to_fit() { - handler->shrink_to_fit(); - } - - /** - * @brief Checks whether the pool of a given component is empty. - * @tparam Component Type of component in which one is interested. - * @return True if the pool of the given component is empty, false - * otherwise. - */ - template - bool empty() const ENTT_NOEXCEPT { - return std::get *>(pools)->empty(); - } - - /** - * @brief Checks whether the group is empty. - * @return True if the group is empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return handler->empty(); - } - - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order of the components. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of components. - */ - template - Component * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a - * valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); - } - - /** - * @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. - * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @return A pointer to the array of entities. - */ - const entity_type * data() const ENTT_NOEXCEPT { - return handler->data(); - } - - /** - * @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 group is empty, the returned iterator will be equal to - * `end()`. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first entity that has the given components. - */ - iterator_type begin() const ENTT_NOEXCEPT { - return handler->begin(); - } - - /** - * @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. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the entity following the last entity that has the - * given components. - */ - iterator_type end() const ENTT_NOEXCEPT { - return handler->end(); - } - - /** - * @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. - */ - iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - const auto it = handler->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. - */ - entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { - return begin()[pos]; - } - - /** - * @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. - */ - bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return find(entt) != end(); - } - - /** - * @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 companion function. - * - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * group doesn't contain the given entity. - * - * @tparam Component Types of components to get. - * @param entt A valid entity identifier. - * @return The components assigned to the entity. - */ - template - decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(contains(entt)); - - if constexpr(sizeof...(Component) == 1) { - return (std::get *>(pools)->get(entt), ...); - } else { - return std::tuple(entt))...>{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 set of references to all its 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, Get &, Other &...); - * void(Get &, Other &...); - * @endcode - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned during iterations. They can be caught only by copy or with - * const references. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - traverse(std::move(func), type_list{}); - } - - /** - * @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 - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void less(Func func) const { - using get_type_list = std::conditional_t, type_list<>, type_list>; - using other_type_list = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), type_list_cat_t{}); - } - - /** - * @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. - * - * The comparison function 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. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `sort` has been invoked. - * - * @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 constexpr(sizeof...(Component) == 0) { - static_assert(std::is_invocable_v); - handler->sort(handler->begin(), handler->end(), std::move(compare), std::move(algo), std::forward(args)...); - } else { - handler->sort(handler->begin(), handler->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - // useless this-> used to suppress a warning with clang - return compare(this->get(lhs), this->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 { - handler->respect(*std::get *>(pools)); - } - -private: - sparse_set *handler; - const std::tuple *, pool_type *...> pools; -}; - - -/** - * @brief Owning group. - * - * Owning groups return all the 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 the owned - * components 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 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 - * 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 overcome the one 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 Type of component owned by the group. - * @tparam Other Other types of components owned by the group. - */ -template -class basic_group, get_t, Owned, Other...> { - /*! @brief A registry is allowed to create groups. */ - friend class basic_registry; - - template - using pool_type = std::conditional_t, const storage>, storage>; - - template - using component_iterator_type = decltype(std::declval>().begin()); - - // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (most likely a bug) - basic_group(const typename basic_registry::size_type *sz, storage> *owned, storage> *... other, storage> *... get) ENTT_NOEXCEPT - : length{sz}, - pools{owned, other..., get...} - {} - - template - void traverse(Func func, type_list, type_list) const { - [[maybe_unused]] auto raw = std::make_tuple((std::get *>(pools)->end() - *length)...); - [[maybe_unused]] auto data = std::get *>(pools)->sparse_set::end() - *length; - - for(auto next = *length; next; --next) { - if constexpr(std::is_invocable_v({}))..., decltype(get({}))...>) { - if constexpr(sizeof...(Weak) == 0) { - func(*(std::get>(raw)++)...); - } else { - const auto entt = *(data++); - func(*(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); - } - } else { - const auto entt = *(data++); - func(entt, *(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); - } - } - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = typename sparse_set::entity_type; - /*! @brief Unsigned integer type. */ - using size_type = typename sparse_set::size_type; - /*! @brief Input iterator type. */ - using iterator_type = typename sparse_set::iterator_type; - - /** - * @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 - size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); - } - - /** - * @brief Returns the number of entities that have the given components. - * @return Number of entities that have the given components. - */ - size_type size() const ENTT_NOEXCEPT { - return *length; - } - - /** - * @brief Checks whether the pool of a given component is empty. - * @tparam Component Type of component in which one is interested. - * @return True if the pool of the given component is empty, false - * otherwise. - */ - template - bool empty() const ENTT_NOEXCEPT { - return std::get *>(pools)->empty(); - } - - /** - * @brief Checks whether the group is empty. - * @return True if the group is empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return !*length; - } - - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty.
- * Moreover, in case the group owns the given component, the range - * `[raw(), raw() + size()]` is such that it contains - * the instances that are part of the group itself. - * - * @note - * There are no guarantees on the order of the components. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of components. - */ - template - Component * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a - * valid range, even if the container is empty.
- * Moreover, in case the group owns the given component, the range - * `[data(), data() + size()]` is such that it - * contains the entities that are part of the group itself. - * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); - } - - /** - * @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. - * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the group in the expected order. - * - * @return A pointer to the array of entities. - */ - const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); - } - - /** - * @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 group is empty, the returned iterator will be equal to - * `end()`. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first entity that has the given components. - */ - iterator_type begin() const ENTT_NOEXCEPT { - return std::get *>(pools)->sparse_set::end() - *length; - } - - /** - * @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. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the entity following the last entity that has the - * given components. - */ - iterator_type end() const ENTT_NOEXCEPT { - return std::get *>(pools)->sparse_set::end(); - } - - /** - * @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. - */ - iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - const auto it = std::get *>(pools)->find(entt); - 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. - */ - entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { - return begin()[pos]; - } - - /** - * @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. - */ - bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return find(entt) != end(); - } - - /** - * @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 companion function. - * - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * group doesn't contain the given entity. - * - * @tparam Component Types of components to get. - * @param entt A valid entity identifier. - * @return The components assigned to the entity. - */ - template - decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(contains(entt)); - - if constexpr(sizeof...(Component) == 1) { - return (std::get *>(pools)->get(entt), ...); - } else { - return std::tuple(entt))...>{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 set of references to all its 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, Owned &, Other &..., Get &...); - * void(Owned &, Other &..., Get &...); - * @endcode - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned during iterations. They can be caught only by copy or with - * const references. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - traverse(std::move(func), type_list{}, type_list{}); - } - - /** - * @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 - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void less(Func func) const { - using owned_type_list = std::conditional_t, type_list<>, type_list>; - using other_type_list = type_list_cat_t, type_list<>, type_list>...>; - using get_type_list = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), type_list_cat_t{}, get_type_list{}); - } - - /** - * @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. - * - * The comparison function 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. - * - * @note - * Attempting to iterate elements using a raw pointer returned by a call to - * either `data` or `raw` gives no guarantees on the order, even though - * `sort` has been invoked. - * - * @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) { - auto *cpool = std::get *>(pools); - - if constexpr(sizeof...(Component) == 0) { - static_assert(std::is_invocable_v); - cpool->sort(cpool->end()-*length, cpool->end(), std::move(compare), std::move(algo), std::forward(args)...); - } else { - cpool->sort(cpool->end()-*length, cpool->end(), [this, compare = std::move(compare)](const entity_type lhs, const entity_type rhs) { - // useless this-> used to suppress a warning with clang - return compare(this->get(lhs), this->get(rhs)); - }, std::move(algo), std::forward(args)...); - } - - for(auto next = *length; next; --next) { - ([next = next-1, curr = cpool->data()[next-1]](auto *cpool) { - const auto pos = cpool->index(curr); - - if(pos != next) { - cpool->swap(next, cpool->index(curr)); - } - }(std::get *>(pools)), ...); - } - } - -private: - const typename basic_registry::size_type *length; - const std::tuple *, pool_type *..., pool_type *...> pools; -}; - - -} - - -#endif // ENTT_ENTITY_GROUP_HPP - -// #include "view.hpp" +inline constexpr get_t get{}; + + +} + + +#endif + +// #include "entity/view.hpp" #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP @@ -6422,17 +27096,172 @@ // #include "../core/type_traits.hpp" +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + // #include "sparse_set.hpp" // #include "storage.hpp" -// #include "entity.hpp" - -// #include "fwd.hpp" - - - -namespace entt { +// #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; /** @@ -6440,12 +27269,8 @@ * * 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 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. + * 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 * @@ -6456,9 +27281,8 @@ * 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. + * 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 @@ -6466,265 +27290,263 @@ * made by means of the registry are immediately reflected by views. * * @warning - * Lifetime of a view must overcome the one of the registry that generated it. + * 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 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 { - static_assert(sizeof...(Component) > 1); - - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; - - template - using pool_type = std::conditional_t, const storage>, storage>; +template +class basic_view_impl, Component...> { + using basic_common_type = basic_sparse_set; template - using component_iterator_type = decltype(std::declval>().begin()); - - using underlying_iterator_type = typename sparse_set::iterator_type; - using unchecked_type = std::array *, (sizeof...(Component) - 1)>; - using traits_type = entt_traits>; - - class iterator { - friend class basic_view; - - iterator(unchecked_type other, underlying_iterator_type first, underlying_iterator_type last) ENTT_NOEXCEPT - : unchecked{other}, - begin{first}, - end{last} - { - if(begin != end && !valid()) { - ++(*this); - } - } - - bool valid() const ENTT_NOEXCEPT { - return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set *view) { - return view->has(*begin); - }); - } + 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 difference_type = typename underlying_iterator_type::difference_type; - using value_type = typename underlying_iterator_type::value_type; - using pointer = typename underlying_iterator_type::pointer; - using reference = typename underlying_iterator_type::reference; - using iterator_category = std::forward_iterator_tag; - - iterator() ENTT_NOEXCEPT = default; - - iterator & operator++() ENTT_NOEXCEPT { - return (++begin != end && !valid()) ? ++(*this) : *this; - } - - iterator operator++(int) ENTT_NOEXCEPT { - iterator orig = *this; - return ++(*this), orig; - } - - bool operator==(const iterator &other) const ENTT_NOEXCEPT { - return other.begin == begin; - } - - bool operator!=(const iterator &other) const ENTT_NOEXCEPT { - return !(*this == other); - } - - pointer operator->() const ENTT_NOEXCEPT { - return begin.operator->(); - } - - reference operator*() const ENTT_NOEXCEPT { - return *operator->(); + 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: - unchecked_type unchecked; - underlying_iterator_type begin; - underlying_iterator_type end; - }; - - // we could use pool_type *..., but vs complains about it and refuses to compile for unknown reasons (likely a bug) - basic_view(storage> *... ref) ENTT_NOEXCEPT - : pools{ref...} - {} - - const sparse_set * candidate() const ENTT_NOEXCEPT { - return std::min({ static_cast *>(std::get *>(pools))... }, [](const auto *lhs, const auto *rhs) { + 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(); }); } - unchecked_type unchecked(const sparse_set *view) const ENTT_NOEXCEPT { - unchecked_type other{}; - typename unchecked_type::size_type pos{}; - ((std::get *>(pools) == view ? nullptr : (other[pos++] = std::get *>(pools))), ...); + [[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; } - template - decltype(auto) get([[maybe_unused]] component_iterator_type it, [[maybe_unused]] pool_type *cpool, [[maybe_unused]] const Entity entt) const ENTT_NOEXCEPT { - if constexpr(std::is_same_v) { - return *it; - } else { - return cpool->get(entt); - } - } - - template - void traverse(Func func, type_list, type_list) const { - const auto end = std::get *>(pools)->sparse_set::end(); - auto begin = std::get *>(pools)->sparse_set::begin(); - - if constexpr(std::disjunction_v...>) { - std::for_each(begin, end, [this, raw = std::get *>(pools)->begin(), &func](const auto entity) mutable { - auto curr = raw++; - - if((std::get *>(pools)->has(entity) && ...)) { - if constexpr(std::is_invocable_v({}))...>) { - func(get(curr, std::get *>(pools), entity)...); + [[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{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { - func(entity, get(curr, std::get *>(pools), entity)...); + std::apply(func, get(entt)); } } - }); - } else { - std::for_each(begin, end, [this, &func](const auto entity) mutable { - if((std::get *>(pools)->has(entity) && ...)) { - if constexpr(std::is_invocable_v({}))...>) { - func(std::get *>(pools)->get(entity)...); + } + } 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{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(it, entt)...)); } else { - func(entity, std::get *>(pools)->get(entity)...); + std::apply(func, std::tuple_cat(dispatch_get(it, entt)...)); } } - }); + + ++it; + } } } public: /*! @brief Underlying entity identifier. */ - using entity_type = typename sparse_set::entity_type; + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using size_type = typename sparse_set::size_type; - /*! @brief Input iterator type. */ - using iterator_type = iterator; - - /** - * @brief Returns the number of existing components of the given type. - * @tparam Comp Type of component of which to return the size. - * @return Number of existing components of the given type. - */ - template - size_type size() const ENTT_NOEXCEPT { - return std::get *>(pools)->size(); - } - - /** - * @brief Estimates the number of entities that have the given components. - * @return Estimated number of entities that have the given components. - */ - size_type size() const ENTT_NOEXCEPT { - return std::min({ std::get *>(pools)->size()... }); - } - - /** - * @brief Checks whether the pool of a given component is empty. - * @tparam Comp Type of component in which one is interested. - * @return True if the pool of the given component is empty, false - * otherwise. - */ - template - bool empty() const ENTT_NOEXCEPT { - return std::get *>(pools)->empty(); - } - - /** - * @brief Checks if the view is definitely empty. - * @return True if the view is definitely empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return (std::get *>(pools)->empty() || ...); - } - - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a valid range, even - * if the container is empty. - * - * @note - * There are no guarantees on the order of the components. Use `begin` and - * `end` if you want to iterate the view in the expected order. - * - * @tparam Comp Type of component in which one is interested. - * @return A pointer to the array of components. + 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 - Comp * raw() const ENTT_NOEXCEPT { - return std::get *>(pools)->raw(); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a valid range, - * even if the container is empty. - * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the view in the expected order. - * - * @tparam Comp Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); - } - - /** - * @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()`. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first entity that has the given components. - */ - iterator_type begin() const ENTT_NOEXCEPT { - const auto *view = candidate(); - return iterator_type{unchecked(view), view->begin(), view->end()}; - } - - /** - * @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 + 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. * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the entity following the last entity that has the - * given components. - */ - iterator_type end() const ENTT_NOEXCEPT { - const auto *view = candidate(); - return iterator_type{unchecked(view), view->end(), view->end()}; + * @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; } /** @@ -6733,138 +27555,54 @@ * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - const auto *view = candidate(); - iterator_type it{unchecked(view), view->find(entt), view->end()}; + [[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. */ - bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return find(entt) != end(); + [[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 companion function. + * 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.
- * An assertion will abort the execution at runtime in debug mode if the - * view doesn't contain the given entity. + * 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 - decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(contains(entt)); - - if constexpr(sizeof...(Comp) == 1) { - return (std::get *>(pools)->get(entt), ...); - } else { - return std::tuple(entt))...>{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 set of references to all its 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, Component &...); - * void(Component &...); - * @endcode - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned during iterations. They can be caught only by copy or with - * const references. - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - const auto *view = candidate(); - ((std::get *>(pools) == view ? each(std::move(func)) : void()), ...); - } - - /** - * @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 all its 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, Component &...); - * void(Component &...); - * @endcode - * - * 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.
- * It is no longer guaranteed that the performance is the best possible, but - * there will be greater control over the order of iteration. - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned during iterations. They can be caught only by copy or with - * const references. - * - * @tparam Comp Type of component to use to enforce the iteration order. - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void each(Func func) const { - using other_type = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), other_type{}, type_list{}); - } - - /** - * @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 - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void less(Func func) const { - const auto *view = candidate(); - ((std::get *>(pools) == view ? less(std::move(func)) : void()), ...); + [[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)...); + } } /** @@ -6882,27 +27620,89 @@ * 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.
- * It is no longer guaranteed that the performance is the best possible, but - * there will be greater control over the order of iteration. + * with that type. * * @sa each * - * @tparam Comp Type of component to use to enforce the iteration order. + * @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 less(Func func) const { - using other_type = type_list_cat_t, type_list<>, type_list>...>; - using non_empty_type = type_list_cat_t, type_list<>, type_list>...>; - traverse(std::move(func), other_type{}, non_empty_type{}); - } - -private: - const std::tuple *...> pools; + 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; }; @@ -6911,10 +27711,7 @@ * * 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.
- * Order of elements during iterations are highly dependent on the order of the - * underlying data structure. See sparse_set and its specializations for more - * details. + * directly and avoid superfluous checks. * * @b Important * @@ -6925,7 +27722,7 @@ * component is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * - * In all the other cases, modifying the pool of the given component in any way + * 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 @@ -6934,116 +27731,225 @@ * made by means of the registry are immediately reflected by views. * * @warning - * Lifetime of a view must overcome the one of the registry that generated it. + * 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 { - /*! @brief A registry is allowed to create views. */ - friend class basic_registry; - - using pool_type = std::conditional_t, const storage>, storage>; - - basic_view(pool_type *ref) ENTT_NOEXCEPT - : pool{ref} - {} - -public: - /*! @brief Type of component iterated by the view. */ - using raw_type = Component; +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 = typename pool_type::entity_type; + using entity_type = Entity; /*! @brief Unsigned integer type. */ - using size_type = typename pool_type::size_type; - /*! @brief Input iterator type. */ - using iterator_type = typename sparse_set::iterator_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. */ - size_type size() const ENTT_NOEXCEPT { - return pool->size(); - } - - /** - * @brief Checks whether the view is empty. + [[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. */ - bool empty() const ENTT_NOEXCEPT { - return pool->empty(); - } - - /** - * @brief Direct access to the list of components. - * - * The returned pointer is such that range `[raw(), raw() + size()]` is - * always a valid range, even if the container is empty. - * - * @note - * There are no guarantees on the order of the components. Use `begin` and - * `end` if you want to iterate the view in the expected order. - * + [[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. */ - raw_type * raw() const ENTT_NOEXCEPT { - return pool->raw(); + [[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 + * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * - * @note - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the view in the expected order. - * * @return A pointer to the array of entities. */ - const entity_type * data() const ENTT_NOEXCEPT { - return pool->data(); - } - - /** - * @brief Returns an iterator to the first entity that has the given - * component. - * - * The returned iterator points to the first entity that has the given - * component. If the view is empty, the returned iterator will be equal to - * `end()`. - * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the first entity that has the given component. - */ - iterator_type begin() const ENTT_NOEXCEPT { - return pool->sparse_set::begin(); - } - - /** - * @brief Returns an iterator that is past the last entity that has the - * given component. - * - * The returned iterator points to the entity following the last entity that - * has the given component. Attempting to dereference the returned iterator + [[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. * - * @note - * Input iterators stay true to the order imposed to the underlying data - * structures. - * - * @return An iterator to the entity following the last entity that has the - * given component. - */ - iterator_type end() const ENTT_NOEXCEPT { - return pool->sparse_set::end(); + * @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; } /** @@ -7052,8 +27958,8 @@ * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ - iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - const auto it = pool->find(entt); + [[nodiscard]] iterator find(const entity_type entt) const { + const auto it = std::get<0>(pools)->find(entt); return it != end() && *it == entt ? it : end(); } @@ -7062,37 +27968,52 @@ * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ - entity_type operator[](const size_type pos) const ENTT_NOEXCEPT { + [[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. */ - bool contains(const entity_type entt) const ENTT_NOEXCEPT { - return find(entt) != end(); + [[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 companion function. - * - * @warning - * Attempting to use an entity that doesn't belong to the view results in - * undefined behavior.
- * An assertion will abort the execution at runtime in debug mode if the - * view doesn't contain the given entity. - * + * 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. */ - decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT { - ENTT_ASSERT(contains(entt)); - return pool->get(entt); + 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); + } } /** @@ -7100,8 +28021,8 @@ * object to them. * * The function object is invoked for each entity. It is provided with the - * entity itself and a reference to its component. The _constness_ of the - * component is as requested.
+ * 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: * @@ -7111,2689 +28032,120 @@ * @endcode * * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned during iterations. They can be caught only by copy or with - * const references. - * - * @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_invocable_v) { - std::for_each(pool->begin(), pool->end(), std::move(func)); - } else { - std::for_each(pool->sparse_set::begin(), pool->sparse_set::end(), [&func, raw = pool->begin()](const auto entt) mutable { - func(entt, *(raw++)); - }); - } - } - - /** - * @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 its 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 in case the component isn't an empty one: - * - * @code{.cpp} - * void(const entity_type, Component &); - * void(Component &); - * @endcode - * - * In case the component is an empty one instead, the following forms are - * accepted: - * - * @code{.cpp} - * void(const entity_type); - * void(); - * @endcode - * - * @sa each - * - * @tparam Func Type of the function object to invoke. - * @param func A valid function object. - */ - template - void less(Func func) const { - if constexpr(std::is_empty_v) { - if constexpr(std::is_invocable_v) { - for(auto pos = pool->size(); pos; --pos) { - func(); - } - } else { - std::for_each(pool->sparse_set::begin(), pool->sparse_set::end(), std::move(func)); - } - } else { - each(std::move(func)); - } - } - -private: - pool_type *pool; -}; - - -} - - -#endif // ENTT_ENTITY_VIEW_HPP - -// #include "fwd.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 and thus - * create views or groups to iterate them. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -class basic_registry { - using context_family = family; - using component_family = family; - using traits_type = entt_traits>; - - struct group_type { - std::size_t owned{}; - }; - - template - struct pool_handler: storage { - group_type *group{}; - - pool_handler() ENTT_NOEXCEPT = default; - - pool_handler(const storage &other) - : storage{other} - {} - - auto on_construct() ENTT_NOEXCEPT { - return sink{construction}; - } - - auto on_replace() ENTT_NOEXCEPT { - return sink{update}; - } - - auto on_destroy() ENTT_NOEXCEPT { - return sink{destruction}; - } - - template - decltype(auto) assign(basic_registry ®istry, const Entity entt, Args &&... args) { - if constexpr(std::is_empty_v) { - storage::construct(entt); - construction.publish(entt, registry, Component{}); - return Component{std::forward(args)...}; - } else { - auto &component = storage::construct(entt, std::forward(args)...); - construction.publish(entt, registry, component); - return component; - } - } - - template - auto batch(basic_registry ®istry, It first, It last, const Comp &... value) { - auto it = storage::batch(first, last, value...); - - if(!construction.empty()) { - std::for_each(first, last, [this, ®istry, it](const auto entt) mutable { - construction.publish(entt, registry, *(it++)); - }); - } - - return it; - } - - void remove(basic_registry ®istry, const Entity entt) { - destruction.publish(entt, registry); - storage::destroy(entt); - } - - template - decltype(auto) replace(basic_registry ®istry, const Entity entt, Args &&... args) { - if constexpr(std::is_empty_v) { - ENTT_ASSERT((storage::has(entt))); - update.publish(entt, registry, Component{}); - return Component{std::forward(args)...}; - } else { - Component component{std::forward(args)...}; - update.publish(entt, registry, component); - return (storage::get(entt) = std::move(component)); - } - } - - private: - using reference_type = std::conditional_t, const Component &, Component &>; - sigh construction{}; - sigh update{}; - sigh destruction{}; - }; - - template - using pool_type = pool_handler>; - - template - struct group_handler; - - template - struct group_handler, get_t>: sparse_set { - std::tuple *..., pool_type *...> cpools{}; - - template - void maybe_valid_if(const Entity entt) { - if constexpr(std::disjunction_v...>) { - if(((std::is_same_v || std::get *>(cpools)->has(entt)) && ...) - && !(std::get *>(cpools)->has(entt) || ...)) - { - this->construct(entt); - } - } else if constexpr(std::disjunction_v...>) { - if((std::get *>(cpools)->has(entt) && ...) - && ((std::is_same_v || !std::get *>(cpools)->has(entt)) && ...)) { - this->construct(entt); - } - } - } - - void discard_if(const Entity entt) { - if(this->has(entt)) { - this->destroy(entt); - } - } - }; - - template - struct group_handler, get_t, Owned...>: group_type { - std::tuple *..., pool_type *..., pool_type *...> cpools{}; - - template - void maybe_valid_if(const Entity entt) { - if constexpr(std::disjunction_v..., std::is_same...>) { - if(((std::is_same_v || std::get *>(cpools)->has(entt)) && ...) - && ((std::is_same_v || std::get *>(cpools)->has(entt)) && ...) - && !(std::get *>(cpools)->has(entt) || ...)) - { - const auto pos = this->owned++; - (std::get *>(cpools)->swap(std::get *>(cpools)->index(entt), pos), ...); - } - } else if constexpr(std::disjunction_v...>) { - if((std::get *>(cpools)->has(entt) && ...) - && (std::get *>(cpools)->has(entt) && ...) - && ((std::is_same_v || !std::get *>(cpools)->has(entt)) && ...)) - { - const auto pos = this->owned++; - (std::get *>(cpools)->swap(std::get *>(cpools)->index(entt), pos), ...); - } - } - } - - void discard_if(const Entity entt) { - if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->index(entt) < this->owned) { - const auto pos = --this->owned; - (std::get *>(cpools)->swap(std::get *>(cpools)->index(entt), pos), ...); - } - } - }; - - struct pool_data { - std::unique_ptr> pool; - void(* remove)(sparse_set &, basic_registry &, const Entity); - std::unique_ptr>(* clone)(const sparse_set &); - void(* stomp)(const sparse_set &, const Entity, basic_registry &, const Entity); - ENTT_ID_TYPE runtime_type; - }; - - struct group_data { - const std::size_t extent[3]; - std::unique_ptr group; - bool(* const is_same)(const component *); - }; - - struct ctx_variable { - std::unique_ptr value; - ENTT_ID_TYPE runtime_type; - }; - - template - static ENTT_ID_TYPE runtime_type() ENTT_NOEXCEPT { - if constexpr(is_named_type_v) { - return named_type_traits::value; - } else { - return Family::template type; - } - } - - void release(const Entity entity) { - // lengthens the implicit list of destroyed entities - const auto entt = to_integer(entity) & traits_type::entity_mask; - const auto version = ((to_integer(entity) >> traits_type::entity_shift) + 1) << traits_type::entity_shift; - const auto node = to_integer(destroyed) | version; - entities[entt] = Entity{node}; - destroyed = Entity{entt}; - } - - template - const pool_type * pool() const ENTT_NOEXCEPT { - const auto ctype = to_integer(type()); - - if constexpr(is_named_type_v) { - const auto it = std::find_if(pools.begin()+skip_family_pools, pools.end(), [ctype](const auto &candidate) { - return candidate.runtime_type == ctype; - }); - - return it == pools.cend() ? nullptr : static_cast *>(it->pool.get()); - } else { - return ctype < skip_family_pools ? static_cast *>(pools[ctype].pool.get()) : nullptr; - } - } - - template - pool_type * pool() ENTT_NOEXCEPT { - return const_cast *>(std::as_const(*this).template pool()); - } - - template - pool_type * assure() { - const auto ctype = to_integer(type()); - pool_data *pdata = nullptr; - - if constexpr(is_named_type_v) { - const auto it = std::find_if(pools.begin()+skip_family_pools, pools.end(), [ctype](const auto &candidate) { - return candidate.runtime_type == ctype; - }); - - pdata = (it == pools.cend() ? &pools.emplace_back() : &(*it)); - } else { - if(!(ctype < skip_family_pools)) { - pools.reserve(pools.size()+ctype-skip_family_pools+1); - - while(!(ctype < skip_family_pools)) { - pools.emplace(pools.begin()+(skip_family_pools++), pool_data{}); - } - } - - pdata = &pools[ctype]; - } - - if(!pdata->pool) { - pdata->runtime_type = ctype; - pdata->pool = std::make_unique>(); - - pdata->remove = [](sparse_set &cpool, basic_registry ®istry, const Entity entt) { - static_cast &>(cpool).remove(registry, entt); - }; - - if constexpr(std::is_copy_constructible_v>) { - pdata->clone = [](const sparse_set &cpool) -> std::unique_ptr> { - return std::make_unique>(static_cast &>(cpool)); - }; - - pdata->stomp = [](const sparse_set &cpool, const Entity from, basic_registry &other, const Entity to) { - other.assign_or_replace(to, static_cast &>(cpool).get(from)); - }; - } else { - pdata->clone = nullptr; - pdata->stomp = nullptr; - } - } - - return static_cast *>(pdata->pool.get()); - } - -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 = typename sparse_set::size_type; - - /*! @brief Default constructor. */ - basic_registry() ENTT_NOEXCEPT = 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 Returns the opaque identifier of a component. - * - * The given component doesn't need to be necessarily in use.
- * Do not use this functionality to generate numeric identifiers for types - * at runtime. They aren't guaranteed to be stable between different runs. - * - * @tparam Component Type of component to query. - * @return Runtime the opaque identifier of the given type of component. - */ - template - static component type() ENTT_NOEXCEPT { - return component{runtime_type()}; - } - - /** - * @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 - size_type size() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->size() : size_type{}; - } - - /** - * @brief Returns the number of entities created so far. - * @return Number of entities created so far. - */ - 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. - */ - size_type alive() const ENTT_NOEXCEPT { - auto curr = destroyed; - size_type cnt{}; - - while(curr != null) { - curr = entities[to_integer(curr) & traits_type::entity_mask]; - ++cnt; - } - - return entities.size() - cnt; - } - - /** - * @brief Increases the capacity of the pool for the given component. - * - * If the new capacity is greater than the current capacity, new storage is - * allocated, otherwise the method does nothing. - * - * @tparam Component Type of component for which to reserve storage. - * @param cap Desired capacity. - */ - template - void reserve(const size_type cap) { - assure()->reserve(cap); - } - - /** - * @brief Increases the capacity of a registry in terms of entities. - * - * 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) { - entities.reserve(cap); - } - - /** - * @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 - size_type capacity() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - 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. - */ - size_type capacity() const ENTT_NOEXCEPT { - return entities.capacity(); - } - - /** - * @brief Requests the removal of unused capacity for a given component. - * @tparam Component Type of component for which to reclaim unused capacity. - */ - template - void shrink_to_fit() { - assure()->shrink_to_fit(); - } - - /** - * @brief Checks whether the pool of a given component is empty. - * @tparam Component Type of component in which one is interested. - * @return True if the pool of the given component is empty, false - * otherwise. - */ - template - bool empty() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->empty() : true; - } - - /** - * @brief Checks if there exists at least an entity still in use. - * @return True if at least an entity is still in use, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return !alive(); - } - - /** - * @brief Direct access to the list of components of a given pool. - * - * The returned pointer is such that range - * `[raw(), raw() + size()]` is always a - * valid range, even if the container is empty. - * - * There are no guarantees on the order of the components. Use a view if you - * want to iterate entities and components in the expected order. - * - * @note - * Empty components aren't explicitly instantiated. Only one instance of the - * given type is created. Therefore, this function always returns a pointer - * to that instance. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of components of the given type. - */ - template - const Component * raw() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->raw() : nullptr; - } - - /*! @copydoc raw */ - template - Component * raw() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).template raw()); - } - - /** - * @brief Direct access to the list of entities of a given pool. - * - * The returned pointer is such that range - * `[data(), data() + size()]` is always a - * valid range, even if the container is empty. - * - * There are no guarantees on the order of the entities. Use a view if you - * want to iterate entities and components in the expected order. - * - * @tparam Component Type of component in which one is interested. - * @return A pointer to the array of entities. - */ - template - const entity_type * data() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->data() : nullptr; - } - - /** - * @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. - */ - bool valid(const entity_type entity) const ENTT_NOEXCEPT { - const auto pos = size_type(to_integer(entity) & traits_type::entity_mask); - return (pos < entities.size() && entities[pos] == entity); - } - - /** - * @brief Returns the entity identifier without the version. - * @param entity An entity identifier, either valid or not. - * @return The entity identifier without the version. - */ - static entity_type entity(const entity_type entity) ENTT_NOEXCEPT { - return entity_type{to_integer(entity) & traits_type::entity_mask}; - } - - /** - * @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. - */ - static version_type version(const entity_type entity) ENTT_NOEXCEPT { - return version_type(to_integer(entity) >> traits_type::entity_shift); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode if the - * registry doesn't own the given entity. - * - * @param entity A valid entity identifier. - * @return Actual version for the given entity identifier. - */ - version_type current(const entity_type entity) const ENTT_NOEXCEPT { - const auto pos = size_type(to_integer(entity) & traits_type::entity_mask); - ENTT_ASSERT(pos < entities.size()); - return version_type(to_integer(entities[pos]) >> traits_type::entity_shift); - } - - /** - * @brief Creates a new entity and returns it. - * - * There are two kinds of entity identifiers: - * - * * Newly created ones in case no entities have been previously destroyed. - * * Recycled ones with updated versions. - * - * Users should not care about the type of the returned entity identifier. - * In case entity identifers are stored around, the `valid` member - * function can be used to know if they are still valid or the entity has - * been destroyed and potentially recycled.
- * The returned entity has assigned the given components, if any. - * - * The components must be at least default constructible. A compilation - * error will occur otherwhise. - * - * @tparam Component Types of components to assign to the entity. - * @return A valid entity identifier if the component list is empty, a tuple - * containing the entity identifier and the references to the components - * just created otherwise. - */ - template - auto create() { - entity_type entities[1]{}; - - if constexpr(sizeof...(Component) == 0) { - create(std::begin(entities), std::end(entities)); - return entities[0]; - } else { - auto it = create(std::begin(entities), std::end(entities)); - return std::tuple(entities[0]))...>{entities[0], *std::get::iterator_type>(it)...}; - } - } - - /** - * @brief Assigns each element in a range an entity. - * - * @sa create - * - * The components must be at least move and default insertable. A - * compilation error will occur otherwhise. - * - * @tparam Component Types of components to assign to the entity. - * @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. - * @return No return value if the component list is empty, a tuple - * containing the iterators to the lists of components just created and - * sorted the same of the entities otherwise. - */ - template - auto create(It first, It last) { - static_assert(std::is_convertible_v::value_type>); - - std::generate(first, last, [this]() { - entity_type curr; - - if(destroyed == null) { - curr = entities.emplace_back(entity_type(entities.size())); - // traits_type::entity_mask is reserved to allow for null identifiers - ENTT_ASSERT(to_integer(curr) < traits_type::entity_mask); - } else { - const auto entt = to_integer(destroyed); - const auto version = to_integer(entities[entt]) & (traits_type::version_mask << traits_type::entity_shift); - destroyed = entity_type{to_integer(entities[entt]) & traits_type::entity_mask}; - curr = entity_type{entt | version}; - entities[entt] = curr; - } - - return curr; - }); - - if constexpr(sizeof...(Component) > 0) { - return std::make_tuple(assure()->batch(*this, first, last)...); - } - } - - /** - * @brief Creates a new entity from a prototype entity. - * - * @sa create - * - * @tparam Component Types of components to copy. - * @tparam Exclude Types of components not to be copied. - * @param src A valid entity identifier to be copied. - * @param other The registry that owns the source entity. - * @return A valid entity identifier. - */ - template - auto create(entity_type src, basic_registry &other, exclude_t = {}) { - entity_type entities[1]{}; - create(std::begin(entities), std::end(entities), src, other, exclude); - return entities[0]; - } - - /** - * @brief Assigns each element in a range an entity from a prototype entity. - * - * @sa create - * - * @tparam Component Types of components to copy. - * @tparam Exclude Types of components not to be copied. - * @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. - * @param src A valid entity identifier to be copied. - * @param other The registry that owns the source entity. - */ - template - void create(It first, It last, entity_type src, basic_registry &other, exclude_t = {}) { - create(first, last); - - if constexpr(sizeof...(Component) == 0) { - stomp(first, last, src, other, exclude); - } else { - static_assert(sizeof...(Component) == 0 || sizeof...(Exclude) == 0); - (assure()->batch(*this, first, last, other.get(src)), ...); - } - } - - /** - * @brief Destroys an entity and lets the registry recycle the identifier. - * - * When an entity is destroyed, its version is updated and the identifier - * can be recycled at any time. In case entity identifers are stored around, - * the `valid` member function can be used to know if they are still valid - * or the entity has been destroyed and potentially recycled. - * - * @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. An assertion will abort the execution - * at runtime in debug mode if a violation is detected. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @param entity A valid entity identifier. - */ - void destroy(const entity_type entity) { - ENTT_ASSERT(valid(entity)); - - for(auto pos = pools.size(); pos; --pos) { - if(auto &pdata = pools[pos-1]; pdata.pool && pdata.pool->has(entity)) { - pdata.remove(*pdata.pool, *this, entity); - } - }; - - // just a way to protect users from listeners that attach components - ENTT_ASSERT(orphan(entity)); - release(entity); - } - - /** - * @brief Destroys all the entities in a range. - * - * @sa destroy - * - * @tparam It Type of input 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 destroy(It first, It last) { - // useless this-> used to suppress a warning with clang - std::for_each(first, last, [this](const auto entity) { this->destroy(entity); }); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity already owns an instance of the given - * component. - * - * @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) assign(const entity_type entity, [[maybe_unused]] Args &&... args) { - ENTT_ASSERT(valid(entity)); - return assure()->assign(*this, entity, std::forward(args)...); - } - - /** - * @brief Removes the given component from an entity. - * - * @warning - * Attempting to use an invalid entity or to remove a component from an - * entity that doesn't own it results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. - * - * @tparam Component Type of component to remove. - * @param entity A valid entity identifier. - */ - template - void remove(const entity_type entity) { - ENTT_ASSERT(valid(entity)); - pool()->remove(*this, entity); - } - - /** - * @brief Checks if an entity has all the given components. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @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 - bool has(const entity_type entity) const ENTT_NOEXCEPT { - ENTT_ASSERT(valid(entity)); - [[maybe_unused]] const auto cpools = std::make_tuple(pool()...); - return ((std::get *>(cpools) ? std::get *>(cpools)->has(entity) : false) && ...); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. - * - * @tparam Component Types of components to get. - * @param entity A valid entity identifier. - * @return References to the components owned by the entity. - */ - template - decltype(auto) get([[maybe_unused]] const entity_type entity) const { - ENTT_ASSERT(valid(entity)); - - if constexpr(sizeof...(Component) == 1) { - return (pool()->get(entity), ...); - } else { - return std::tuple(entity))...>{get(entity)...}; - } - } - - /*! @copydoc get */ - template - decltype(auto) get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT { - ENTT_ASSERT(valid(entity)); - - if constexpr(sizeof...(Component) == 1) { - return (pool()->get(entity), ...); - } else { - return std::tuple(entity))...>{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.has(entity) ? registry.get(entity) : registry.assign(entity, args...); - * @endcode - * - * Prefer this function anyway because it has slightly better performance. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @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 - decltype(auto) get_or_assign(const entity_type entity, Args &&... args) ENTT_NOEXCEPT { - ENTT_ASSERT(valid(entity)); - auto *cpool = assure(); - return cpool->has(entity) ? cpool->get(entity) : cpool->assign(*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.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @tparam Component Types of components to get. - * @param entity A valid entity identifier. - * @return Pointers to the components owned by the entity. - */ - template - auto try_get([[maybe_unused]] const entity_type entity) const ENTT_NOEXCEPT { - ENTT_ASSERT(valid(entity)); - - if constexpr(sizeof...(Component) == 1) { - const auto cpools = std::make_tuple(pool()...); - return ((std::get *>(cpools) ? std::get *>(cpools)->try_get(entity) : nullptr), ...); - } else { - return std::tuple *...>{try_get(entity)...}; - } - } - - /*! @copydoc try_get */ - template - auto try_get([[maybe_unused]] const entity_type entity) ENTT_NOEXCEPT { - if constexpr(sizeof...(Component) == 1) { - return (const_cast(std::as_const(*this).template try_get(entity)), ...); - } else { - return std::tuple{try_get(entity)...}; - } - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity or if the entity doesn't own an instance of the given - * component. - * - * @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 newly created component. - */ - template - decltype(auto) replace(const entity_type entity, Args &&... args) { - ENTT_ASSERT(valid(entity)); - return pool()->replace(*this, entity, std::forward(args)...); - } - - /** - * @brief Assigns or replaces the given component for an entity. - * - * Equivalent to the following snippet (pseudocode): - * - * @code{.cpp} - * auto &component = registry.has(entity) ? registry.replace(entity, args...) : registry.assign(entity, args...); - * @endcode - * - * Prefer this function anyway because it has slightly better performance. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @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) assign_or_replace(const entity_type entity, Args &&... args) { - ENTT_ASSERT(valid(entity)); - auto *cpool = assure(); - return cpool->has(entity) ? cpool->replace(*this, entity, std::forward(args)...) : cpool->assign(*this, entity, std::forward(args)...); - } - - /** - * @brief Returns a sink object for the given component. - * - * A sink is an opaque object used to connect listeners to components.
- * 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(Entity, registry &, Component &); - * @endcode - * - * Listeners are invoked **after** the component has been assigned to the - * entity. The order of invocation of the listeners isn't guaranteed. - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned through signals. They can be caught only by copy or with - * const references. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - auto on_construct() ENTT_NOEXCEPT { - return assure()->on_construct(); - } - - /** - * @brief Returns a sink object for the given component. - * - * A sink is an opaque object used to connect listeners to components.
- * The sink returned by this function can be used to receive notifications - * whenever an instance of the given component is explicitly replaced. - * - * The function type for a listener is equivalent to: - * - * @code{.cpp} - * void(Entity, registry &, Component &); - * @endcode - * - * Listeners are invoked **before** the component has been replaced. The - * order of invocation of the listeners isn't guaranteed. - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned through signals. They can be caught only by copy or with - * const references. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - auto on_replace() ENTT_NOEXCEPT { - return assure()->on_replace(); - } - - /** - * @brief Returns a sink object for the given component. - * - * A sink is an opaque object used to connect listeners to components.
- * 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(Entity, registry &); - * @endcode - * - * Listeners are invoked **before** the component has been removed from the - * entity. The order of invocation of the listeners isn't guaranteed. - * - * @note - * Empty types aren't explicitly instantiated. Therefore, temporary objects - * are returned through signals. They can be caught only by copy or with - * const references. - * - * @sa sink - * - * @tparam Component Type of component of which to get the sink. - * @return A temporary sink object. - */ - template - auto on_destroy() ENTT_NOEXCEPT { - return assure()->on_destroy(); - } - - /** - * @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 are only partially sorted.
- * In other words, only the elements that aren't part of the group are - * sorted by this function. Use the `sort` member function of a group to - * sort the other half of the pool. - * - * @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) { - if(auto *cpool = assure(); cpool->group) { - const auto last = cpool->end() - cpool->group->owned; - cpool->sort(cpool->begin(), last, std::move(compare), std::move(algo), std::forward(args)...); - } else { - cpool->sort(cpool->begin(), cpool->end(), 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 this way.
- * An assertion will abort the execution at runtime in debug mode in case - * the pool is owned by a group. - * - * @tparam To Type of components to sort. - * @tparam From Type of components to use to sort. - */ - template - void sort() { - ENTT_ASSERT(!owned()); - assure()->respect(*assure()); - } - - /** - * @brief Resets the given component for an entity. - * - * If the entity has an instance of the component, this function removes the - * component from the entity. Otherwise it does nothing. - * - * @warning - * Attempting to use an invalid entity results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entity. - * - * @tparam Component Type of component to reset. - * @param entity A valid entity identifier. - */ - template - void reset(const entity_type entity) { - ENTT_ASSERT(valid(entity)); - - if(auto *cpool = assure(); cpool->has(entity)) { - cpool->remove(*this, entity); - } - } - - /** - * @brief Resets the pool of the given component. - * - * For each entity that has an instance of the given component, the - * component itself is removed and thus destroyed. - * - * @tparam Component Type of component whose pool must be reset. - */ - template - void reset() { - if(auto *cpool = assure(); cpool->on_destroy().empty()) { - // no group set, otherwise the signal wouldn't be empty - cpool->reset(); - } else { - for(const auto entity: static_cast &>(*cpool)) { - cpool->remove(*this, entity); - } - } - } - - /** - * @brief Resets a whole registry. - * - * Destroys all the entities. After a call to `reset`, all the entities - * still in use are recycled with a new version number. In case entity - * identifers are stored around, the `valid` member function can be used - * to know if they are still valid. - */ - void reset() { - each([this](const auto entity) { - // useless this-> used to suppress a warning with clang - this->destroy(entity); - }); - } - - /** - * @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. + * 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 { - static_assert(std::is_invocable_v); - - if(destroyed == null) { - for(auto pos = entities.size(); pos; --pos) { - func(entities[pos-1]); - } - } else { - for(auto pos = entities.size(); pos; --pos) { - const auto curr = entity_type(pos - 1); - const auto entity = entities[to_integer(curr)]; - const auto entt = entity_type{to_integer(entity) & traits_type::entity_mask}; - - if(curr == entt) { + 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); } } - } - } - - /** - * @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. - */ - bool orphan(const entity_type entity) const { - ENTT_ASSERT(valid(entity)); - bool orphan = true; - - for(std::size_t pos{}, last = pools.size(); pos < last && orphan; ++pos) { - const auto &pdata = pools[pos]; - orphan = !(pdata.pool && pdata.pool->has(entity)); - } - - return orphan; - } - - /** - * @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 { - static_assert(std::is_invocable_v); - - each([this, &func](const auto entity) { - if(orphan(entity)) { - func(entity); - } - }); - } - - /** - * @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. - * @return A newly created view. - */ - template - entt::basic_view view() { - return { assure()... }; - } - - /*! @copydoc view */ - template - entt::basic_view view() const { - static_assert(std::conjunction_v...>); - return const_cast(this)->view(); - } - - /** - * @brief Checks whether a given component belongs to a group. - * @tparam Component Type of component in which one is interested. - * @return True if the component belongs to a group, false otherwise. - */ - template - bool owned() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool && cpool->group; - } - - /** - * @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 - entt::basic_group, get_t, Owned...> group(get_t, exclude_t = {}) { - static_assert(sizeof...(Owned) + sizeof...(Get) > 0); - static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1); - - using handler_type = group_handler, get_t, Owned...>; - - const std::size_t extent[] = { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }; - const component types[] = { type()..., type()..., type()... }; - handler_type *curr = nullptr; - - if(auto it = std::find_if(groups.begin(), groups.end(), [&extent, &types](auto &&gdata) { - return std::equal(std::begin(extent), std::end(extent), gdata.extent) && gdata.is_same(types); - }); it != groups.cend()) - { - curr = static_cast(it->group.get()); - } - - if(!curr) { - groups.push_back(group_data{ - { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }, - decltype(group_data::group){new handler_type{}, [](void *gptr) { delete static_cast(gptr); }}, - [](const component *other) { - const component ctypes[] = { type()..., type()..., type()... }; - return std::equal(std::begin(ctypes), std::end(ctypes), other); - } - }); - - curr = static_cast(groups.back().group.get()); - - ((std::get *>(curr->cpools) = assure()), ...); - ((std::get *>(curr->cpools) = assure()), ...); - ((std::get *>(curr->cpools) = assure()), ...); - - ENTT_ASSERT((!std::get *>(curr->cpools)->group && ...)); - - ((std::get *>(curr->cpools)->group = curr), ...); - (std::get *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if>(*curr), ...); - (std::get *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...); - - (std::get *>(curr->cpools)->on_construct().template connect<&handler_type::template maybe_valid_if>(*curr), ...); - (std::get *>(curr->cpools)->on_destroy().template connect<&handler_type::discard_if>(*curr), ...); - - (std::get *>(curr->cpools)->on_destroy().template connect<&handler_type::template maybe_valid_if>(*curr), ...); - (std::get *>(curr->cpools)->on_construct().template connect<&handler_type::discard_if>(*curr), ...); - - const auto *cpool = std::min({ - static_cast *>(std::get *>(curr->cpools))..., - static_cast *>(std::get *>(curr->cpools))... - }, [](const auto *lhs, const auto *rhs) { - return lhs->size() < rhs->size(); - }); - - // we cannot iterate backwards because we want to leave behind valid entities in case of owned types - std::for_each(cpool->data(), cpool->data() + cpool->size(), [curr](const auto entity) { - if((std::get *>(curr->cpools)->has(entity) && ...) - && (std::get *>(curr->cpools)->has(entity) && ...) - && !(std::get *>(curr->cpools)->has(entity) || ...)) - { - if constexpr(sizeof...(Owned) == 0) { - curr->construct(entity); - } else { - const auto pos = curr->owned++; - // useless this-> used to suppress a warning with clang - (std::get *>(curr->cpools)->swap(std::get *>(curr->cpools)->index(entity), pos), ...); - } - } - }); - } - - if constexpr(sizeof...(Owned) == 0) { - return { static_cast *>(curr), std::get *>(curr->cpools)... }; - } else { - return { &curr->owned, std::get *>(curr->cpools)... , std::get *>(curr->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 - entt::basic_group, get_t, Owned...> group(get_t, exclude_t = {}) const { - static_assert(std::conjunction_v..., std::is_const...>); - return const_cast(this)->group(entt::get, exclude); - } - - /** - * @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 - entt::basic_group, get_t<>, Owned...> group(exclude_t = {}) { - return group(entt::get<>, exclude); - } - - /** - * @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 - entt::basic_group, get_t<>, Owned...> group(exclude_t = {}) const { - static_assert(std::conjunction_v...>); - return const_cast(this)->group(exclude); - } - - /** - * @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.
- * This is particularly well suited to plugin systems and mods in general. - * - * @tparam It Type of input iterator. - * @param first An iterator to the first element of the range of components. - * @param last An iterator past the last element of the range of components. - * @return A newly created runtime view. - */ - template - entt::basic_runtime_view runtime_view(It first, It last) const { - static_assert(std::is_same_v::value_type, component>); - std::vector *> set(std::distance(first, last)); - - std::transform(first, last, set.begin(), [this](const component ctype) { - auto it = std::find_if(pools.begin(), pools.end(), [ctype = to_integer(ctype)](const auto &pdata) { - return pdata.pool && pdata.runtime_type == ctype; - }); - - return it != pools.cend() && it->pool ? it->pool.get() : nullptr; - }); - - return { std::move(set) }; - } - - /** - * @brief Returns a full or partial copy of a registry. - * - * The components must be copyable for obvious reasons. The entities - * maintain their versions once copied.
- * If no components are provided, the registry will try to clone all the - * existing pools. The ones for non-copyable types won't be cloned. - * - * This feature supports exclusion lists. The excluded types have higher - * priority than those indicated for cloning. An excluded type will never be - * cloned. - * - * @note - * There isn't an efficient way to know if all the entities are assigned at - * least one component once copied. Therefore, there may be orphans. It is - * up to the caller to clean up the registry if necessary. + } 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 - * Listeners and groups aren't copied. It is up to the caller to connect the - * listeners of interest to the new registry and to set up groups. - * - * @warning - * Attempting to clone components that aren't copyable results in unexpected - * behaviors.
- * A static assertion will abort the compilation when the components - * provided aren't copy constructible. Otherwise, an assertion will abort - * the execution at runtime in debug mode in case one or more pools cannot - * be cloned. - * - * @tparam Component Types of components to clone. - * @tparam Exclude Types of components not to be cloned. - * @return A fresh copy of the registry. - */ - template - basic_registry clone(exclude_t = {}) const { - static_assert(std::conjunction_v...>); - basic_registry other; - - other.pools.resize(pools.size()); - - for(auto pos = pools.size(); pos; --pos) { - const auto &pdata = pools[pos-1]; - ENTT_ASSERT(!sizeof...(Component) || !pdata.pool || pdata.clone); - - if(pdata.pool && pdata.clone - && (!sizeof...(Component) || ... || (pdata.runtime_type == to_integer(type()))) - && ((pdata.runtime_type != to_integer(type())) && ...)) - { - auto &curr = other.pools[pos-1]; - curr.remove = pdata.remove; - curr.clone = pdata.clone; - curr.stomp = pdata.stomp; - curr.pool = pdata.clone ? pdata.clone(*pdata.pool) : nullptr; - curr.runtime_type = pdata.runtime_type; - } - } - - other.skip_family_pools = skip_family_pools; - other.destroyed = destroyed; - other.entities = entities; - - other.pools.erase(std::remove_if(other.pools.begin()+skip_family_pools, other.pools.end(), [](const auto &pdata) { - return !pdata.pool; - }), other.pools.end()); - - return other; - } - - /** - * @brief Stomps an entity and its components. - * - * The components must be copyable for obvious reasons. The entities - * must be both valid.
- * If no components are provided, the registry will try to copy all the - * existing types. The non-copyable ones will be ignored. - * - * This feature supports exclusion lists as an alternative to component - * lists. An excluded type will never be copied. - * - * @warning - * Attempting to copy components that aren't copyable results in unexpected - * behaviors.
- * A static assertion will abort the compilation when the components - * provided aren't copy constructible. Otherwise, an assertion will abort - * the execution at runtime in debug mode in case one or more types cannot - * be copied. - * - * @warning - * Attempting to use invalid entities results in undefined behavior.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid entities. - * - * @tparam Component Types of components to copy. - * @tparam Exclude Types of components not to be copied. - * @param dst A valid entity identifier to copy to. - * @param src A valid entity identifier to be copied. - * @param other The registry that owns the source entity. - */ - template - void stomp(const entity_type dst, const entity_type src, basic_registry &other, exclude_t = {}) { - const entity_type entities[1]{dst}; - stomp(std::begin(entities), std::end(entities), src, other, exclude); - } - - /** - * @brief Stomps the entities in a range and their components. - * - * @sa stomp - * - * @tparam Component Types of components to copy. - * @tparam Exclude Types of components not to be copied. - * @param first An iterator to the first element of the range to stomp. - * @param last An iterator past the last element of the range to stomp. - * @param src A valid entity identifier to be copied. - * @param other The registry that owns the source entity. - */ - template - void stomp(It first, It last, const entity_type src, basic_registry &other, exclude_t = {}) { - static_assert(sizeof...(Component) == 0 || sizeof...(Exclude) == 0); - static_assert(std::conjunction_v...>); - - for(auto pos = other.pools.size(); pos; --pos) { - const auto &pdata = other.pools[pos-1]; - ENTT_ASSERT(!sizeof...(Component) || !pdata.pool || pdata.stomp); - - if(pdata.pool && pdata.stomp - && (!sizeof...(Component) || ... || (pdata.runtime_type == to_integer(type()))) - && ((pdata.runtime_type != to_integer(type())) && ...) - && pdata.pool->has(src)) - { - std::for_each(first, last, [this, &pdata, src](const auto entity) { - pdata.stomp(*pdata.pool, src, *this, entity); - }); - } - } - } - - /** - * @brief Returns a temporary object to use to create snapshots. - * - * A snapshot is either a full or a partial dump of a registry.
- * It can be used to save and restore its internal state or to keep two or - * more instances of this class in sync, as an example in a client-server - * architecture. - * - * @return A temporary object to use to take snasphosts. - */ - entt::basic_snapshot snapshot() const ENTT_NOEXCEPT { - using follow_fn_type = entity_type(const basic_registry &, const entity_type); - - const auto head = to_integer(destroyed); - const entity_type seed = (destroyed == null) ? destroyed : entity_type{head | (to_integer(entities[head]) & (traits_type::version_mask << traits_type::entity_shift))}; - - follow_fn_type *follow = [](const basic_registry ®, const entity_type entity) -> entity_type { - const auto &others = reg.entities; - const auto entt = to_integer(entity) & traits_type::entity_mask; - const auto curr = to_integer(others[entt]) & traits_type::entity_mask; - return entity_type{curr | (to_integer(others[curr]) & (traits_type::version_mask << traits_type::entity_shift))}; - }; - - return { this, seed, follow }; - } - - /** - * @brief Returns a temporary object to use to load snapshots. - * - * A snapshot is either a full or a partial dump of a registry.
- * It can be used to save and restore its internal state or to keep two or - * more instances of this class in sync, as an example in a client-server - * architecture. - * - * @note - * The loader returned by this function requires that the registry be empty. - * In case it isn't, all the data will be automatically deleted before to - * return. - * - * @return A temporary object to use to load snasphosts. - */ - basic_snapshot_loader loader() ENTT_NOEXCEPT { - using force_fn_type = void(basic_registry &, const entity_type, const bool); - - force_fn_type *force = [](basic_registry ®, const entity_type entity, const bool discard) { - const auto entt = to_integer(entity) & traits_type::entity_mask; - auto &others = reg.entities; - - if(!(entt < others.size())) { - auto curr = others.size(); - others.resize(entt + 1); - - std::generate(others.data() + curr, others.data() + entt, [curr]() mutable { - return entity_type(curr++); - }); - } - - others[entt] = entity; - - if(discard) { - reg.destroy(entity); - const auto version = to_integer(entity) & (traits_type::version_mask << traits_type::entity_shift); - others[entt] = entity_type{(to_integer(others[entt]) & traits_type::entity_mask) | version}; - } - }; - - reset(); - entities.clear(); - destroyed = null; - - return { this, force }; - } - - /** - * @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) { - const auto ctype = runtime_type(); - auto it = std::find_if(vars.begin(), vars.end(), [ctype](const auto &candidate) { - return candidate.runtime_type == ctype; - }); - - if(it == vars.cend()) { - vars.push_back({ - decltype(ctx_variable::value){new Type{std::forward(args)...}, [](void *ptr) { delete static_cast(ptr); }}, - ctype - }); - - it = std::prev(vars.end()); - } else { - it->value.reset(new Type{std::forward(args)...}); - } - - return *static_cast(it->value.get()); - } - - /** - * @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(), [](auto &var) { - return var.runtime_type == runtime_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 Reference to the object. - */ - template - Type & ctx_or_set(Args &&... args) { - auto *type = try_ctx(); - return type ? *type : 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 - const Type * try_ctx() const ENTT_NOEXCEPT { - const auto it = std::find_if(vars.begin(), vars.end(), [](const auto &var) { - return var.runtime_type == runtime_type(); - }); - - return (it == vars.cend()) ? nullptr : static_cast(it->value.get()); - } - - /*! @copydoc try_ctx */ - template - Type * try_ctx() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).template try_ctx()); - } - - /** - * @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.
- * An assertion will abort the execution at runtime in debug mode in case of - * invalid requests. - * - * @tparam Type Type of object to get. - * @return A valid reference to the object in the context of the registry. - */ - template - const Type & ctx() const ENTT_NOEXCEPT { - const auto *instance = try_ctx(); - ENTT_ASSERT(instance); - return *instance; - } - - /*! @copydoc ctx */ - template - Type & ctx() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).template ctx()); - } - -private: - std::size_t skip_family_pools{}; - std::vector pools{}; - std::vector groups{}; - std::vector vars{}; - std::vector entities{}; - entity_type destroyed{null}; -}; - - -} - - -#endif // ENTT_ENTITY_REGISTRY_HPP - -// #include "entity.hpp" - -// #include "fwd.hpp" - - - -namespace entt { - - -/** - * @brief Dedicated to those who aren't confident with the - * entity-component-system architecture. - * - * Tiny wrapper around a registry, for all those users that aren't confident - * with entity-component-system architecture and prefer to iterate objects - * directly. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct basic_actor { - /*! @brief Type of registry used internally. */ - using registry_type = basic_registry; - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - - basic_actor() ENTT_NOEXCEPT - : entt{entt::null}, reg{nullptr} - {} - - /** - * @brief Constructs an actor from a given registry. - * @param ref An instance of the registry class. - */ - explicit basic_actor(registry_type &ref) - : entt{ref.create()}, reg{&ref} - {} - - /** - * @brief Constructs an actor from a given entity. - * @param entity A valid entity identifier. - * @param ref An instance of the registry class. - */ - explicit basic_actor(entity_type entity, registry_type &ref) - : entt{entity}, reg{&ref} - { - ENTT_ASSERT(ref.valid(entity)); - } - - /*! @brief Default destructor. */ - virtual ~basic_actor() { - if(*this) { - reg->destroy(entt); - } - } - - /** - * @brief Move constructor. - * - * After actor move construction, instances that have been moved from are - * placed in a valid but unspecified state. It's highly discouraged to - * continue using them. - * - * @param other The instance to move from. - */ - basic_actor(basic_actor &&other) - : entt{other.entt}, reg{other.reg} - { - other.entt = null; - } - - /** - * @brief Move assignment operator. - * - * After actor move assignment, instances that have been moved from are - * placed in a valid but unspecified state. It's highly discouraged to - * continue using them. - * - * @param other The instance to move from. - * @return This actor. - */ - basic_actor & operator=(basic_actor &&other) { - if(this != &other) { - auto tmp{std::move(other)}; - std::swap(reg, tmp.reg); - std::swap(entt, tmp.entt); - } - - return *this; - } - - /** - * @brief Assigns the given component to an actor. - * - * 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 actor.
- * In case the actor already has a component of the given type, it's - * replaced with the new one. - * - * @tparam Component Type of the 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) assign(Args &&... args) { - return reg->template assign_or_replace(entt, std::forward(args)...); - } - - /** - * @brief Removes the given component from an actor. - * @tparam Component Type of the component to remove. - */ - template - void remove() { - reg->template remove(entt); - } - - /** - * @brief Checks if an actor has the given components. - * @tparam Component Components for which to perform the check. - * @return True if the actor has all the components, false otherwise. - */ - template - bool has() const ENTT_NOEXCEPT { - return (reg->template has(entt) && ...); - } - - /** - * @brief Returns references to the given components for an actor. - * @tparam Component Types of components to get. - * @return References to the components owned by the actor. - */ - template - decltype(auto) get() const ENTT_NOEXCEPT { - return std::as_const(*reg).template get(entt); - } - - /*! @copydoc get */ - template - decltype(auto) get() ENTT_NOEXCEPT { - return reg->template get(entt); - } - - /** - * @brief Returns pointers to the given components for an actor. - * @tparam Component Types of components to get. - * @return Pointers to the components owned by the actor. - */ - template - auto try_get() const ENTT_NOEXCEPT { - return std::as_const(*reg).template try_get(entt); - } - - /*! @copydoc try_get */ - template - auto try_get() ENTT_NOEXCEPT { - return reg->template try_get(entt); - } - - /** - * @brief Returns a reference to the underlying registry. - * @return A reference to the underlying registry. - */ - const registry_type & backend() const ENTT_NOEXCEPT { - return *reg; - } - - /*! @copydoc backend */ - registry_type & backend() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).backend()); - } - - /** - * @brief Returns the entity associated with an actor. - * @return The entity associated with the actor. - */ - entity_type entity() const ENTT_NOEXCEPT { - return entt; - } - - /** - * @brief Checks if an actor refers to a valid entity or not. - * @return True if the actor refers to a valid entity, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return reg && reg->valid(entt); - } - -private: - entity_type entt; - registry_type *reg; -}; - - -} - - -#endif // ENTT_ENTITY_ACTOR_HPP - -// #include "entity/entity.hpp" - -// #include "entity/group.hpp" - -// #include "entity/helper.hpp" -#ifndef ENTT_ENTITY_HELPER_HPP -#define ENTT_ENTITY_HELPER_HPP - - -#include -// #include "../config/config.h" - -// #include "../signal/sigh.hpp" - -// #include "registry.hpp" - - - -namespace entt { - - -/** - * @brief Converts a registry to a view. - * @tparam Const Constness of the accepted registry. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_view { - /*! @brief Type of registry to convert. */ - using registry_type = std::conditional_t, entt::basic_registry>; - - /** - * @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 Component Type of components used to construct the view. - * @return A newly created view. - */ - template - operator entt::basic_view() const { - return reg.template view(); - } - -private: - registry_type ® + * 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. - * - * It allows to deduce the constness of a registry directly from the instance - * provided to the constructor. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_view(basic_registry &) ENTT_NOEXCEPT -> as_view; - - -/*! @copydoc as_view */ -template -as_view(const basic_registry &) ENTT_NOEXCEPT -> as_view; - - -/** - * @brief Converts a registry to a group. - * @tparam Const Constness of the accepted registry. - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -struct as_group { - /*! @brief Type of registry to convert. */ - using registry_type = std::conditional_t, entt::basic_registry>; - - /** - * @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 entt::basic_group() const { - return reg.template group(Get{}, Exclude{}); - } - -private: - registry_type ® -}; - - -/** - * @brief Deduction guide. - * - * It allows to deduce the constness of a registry directly from the instance - * provided to the constructor. - * - * @tparam Entity A valid entity type (see entt_traits for more details). - */ -template -as_group(basic_registry &) ENTT_NOEXCEPT -> as_group; - - -/*! @copydoc as_group */ -template -as_group(const basic_registry &) ENTT_NOEXCEPT -> as_group; - - -/** - * @brief Alias template to ease the assignment of tags to entities. - * - * If used in combination with hashed strings, it simplifies the assignment of - * tags to entities and the use of tags in general where a type would be - * required otherwise.
- * As an example and where the user defined literal for hashed strings hasn't - * been changed: - * @code{.cpp} - * entt::registry registry; - * registry.assign>(entity); - * @endcode - * - * @note - * Tags are empty components and therefore candidates for the empty component - * optimization. - * - * @tparam Value The numeric representation of an instance of hashed string. - */ -template -using tag = std::integral_constant; - - -} - - -#endif // ENTT_ENTITY_HELPER_HPP - -// #include "entity/observer.hpp" -#ifndef ENTT_ENTITY_OBSERVER_HPP -#define ENTT_ENTITY_OBSERVER_HPP - - -#include -#include -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/type_traits.hpp" - -// #include "registry.hpp" - -// #include "storage.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, type_list>>{}; - } - - /** - * @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 replace() ENTT_NOEXCEPT { - return basic_collector, type_list<>>, AnyOf>>{}; - } -}; - -/** - * @brief Collector. - * @copydetails basic_collector<> - * @tparam AnyOf Types of components for which changes should be detected. - * @tparam Matcher Types of grouping matchers. - */ -template -struct basic_collector, type_list>, Rule...>, Other...> { - /** - * @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 { - using first = matcher, type_list>, Rule...>; - return basic_collector, type_list<>>, type_list, type_list>>{}; - } - - /** - * @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 replace() ENTT_NOEXCEPT { - using first = matcher, type_list>, Rule...>; - return basic_collector, type_list<>>, AnyOf>>{}; - } - - /** - * @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 { - return basic_collector, type_list>, Rule...>, Other...>{}; - } -}; - - -/*! @brief Variable template used to ease the definition of collectors. */ -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 explicitly - * replaced 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 the one 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, const Entity entt, const basic_registry ®) { - if(reg.template has(entt) && !(reg.template has(entt) || ...)) { - auto *comp = obs.view.try_get(entt); - (comp ? *comp : obs.view.construct(entt)) |= (1 << Index); - } - } - - template - static void discard_if(basic_observer &obs, const Entity entt) { - if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { - obs.view.destroy(entt); - } - } - - template - static void connect(basic_observer &obs, basic_registry ®) { - (reg.template on_destroy().template connect<&discard_if>(obs), ...); - (reg.template on_construct().template connect<&discard_if>(obs), ...); - reg.template on_replace().template connect<&maybe_valid_if>(obs); - reg.template on_destroy().template connect<&discard_if>(obs); - } - - static void disconnect(basic_observer &obs, basic_registry ®) { - (reg.template on_destroy().disconnect(obs), ...); - (reg.template on_construct().disconnect(obs), ...); - reg.template on_replace().disconnect(obs); - reg.template on_destroy().disconnect(obs); - } - }; - - template - struct matcher_handler, type_list>, type_list, type_list>> { - template - static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry ®) { - if(reg.template has(entt) && !(reg.template has(entt) || ...) - && reg.template has(entt) && !(reg.template has(entt) || ...)) - { - auto *comp = obs.view.try_get(entt); - (comp ? *comp : obs.view.construct(entt)) |= (1 << Index); - } - } - - template - static void discard_if(basic_observer &obs, const Entity entt) { - if(auto *value = obs.view.try_get(entt); value && !(*value &= (~(1 << Index)))) { - obs.view.destroy(entt); - } - } - - template - static void connect(basic_observer &obs, basic_registry ®) { - (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.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_observer &obs, basic_registry ®) { - (matcher_handler::disconnect(obs, reg), ...); - } - - template - void connect(basic_registry ®, std::index_sequence) { - static_assert(sizeof...(Matcher) < std::numeric_limits::digits); - (matcher_handler::template connect(*this, reg), ...); - release = &basic_observer::disconnect; - } - -public: - /*! @brief Underlying entity identifier. */ - using entity_type = Entity; - /*! @brief Unsigned integer type. */ - using size_type = typename sparse_set::size_type; - /*! @brief Input iterator type. */ - using iterator_type = typename sparse_set::iterator_type; - - /*! @brief Default constructor. */ - basic_observer() ENTT_NOEXCEPT - : target{}, release{}, view{} - {} - - /*! @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 ®, basic_collector) ENTT_NOEXCEPT - : target{®}, - release{}, - view{} - { - connect(reg, std::make_index_sequence{}); - } - - /*! @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 ®, basic_collector) { - disconnect(); - connect(reg, std::make_index_sequence{}); - target = ® - view.reset(); - } - - /*! @brief Disconnects an observer from the registry it keeps track of. */ - void disconnect() { - if(release) { - release(*this, *target); - release = nullptr; - } - } - - /** - * @brief Returns the number of elements in an observer. - * @return Number of elements. - */ - size_type size() const ENTT_NOEXCEPT { - return view.size(); - } - - /** - * @brief Checks whether an observer is empty. - * @return True if the observer is empty, false otherwise. - */ - bool empty() const ENTT_NOEXCEPT { - return view.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 - * There are no guarantees on the order of the entities. Use `begin` and - * `end` if you want to iterate the observer in the expected order. - * - * @return A pointer to the array of entities. - */ - const entity_type * data() const ENTT_NOEXCEPT { - return view.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. - */ - iterator_type begin() const ENTT_NOEXCEPT { - return view.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. - */ - iterator_type end() const ENTT_NOEXCEPT { - return view.sparse_set::end(); - } - - /*! @brief Resets the underlying container. */ - void clear() { - view.reset(); - } - - /** - * @brief Iterates entities and applies the given function object to them, - * then clears the observer. - * - * 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 { - static_assert(std::is_invocable_v); - std::for_each(begin(), end(), std::move(func)); - } - - /** - * @brief Iterates entities and applies the given function object to them, - * then clears the observer. - * - * 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) { - std::as_const(*this).each(std::move(func)); - clear(); - } - -private: - basic_registry *target; - void(* release)(basic_observer &, basic_registry &); - storage view; -}; - - -} - - -#endif // ENTT_ENTITY_OBSERVER_HPP - -// #include "entity/registry.hpp" - -// #include "entity/runtime_view.hpp" - -// #include "entity/snapshot.hpp" - -// #include "entity/sparse_set.hpp" - -// #include "entity/storage.hpp" - -// #include "entity/utility.hpp" - -// #include "entity/view.hpp" + * @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 @@ -9807,49 +28159,87 @@ #define ENTT_CONFIG_CONFIG_H -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC +#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 // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +# 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 @@ -9881,7 +28271,7 @@ * @brief Tests if a valid service implementation is set. * @return True if the service is set, false otherwise. */ - static bool empty() ENTT_NOEXCEPT { + [[nodiscard]] static bool empty() ENTT_NOEXCEPT { return !static_cast(service); } @@ -9895,7 +28285,7 @@ * * @return A reference to the service implementation currently set, if any. */ - static std::weak_ptr get() ENTT_NOEXCEPT { + [[nodiscard]] static std::weak_ptr get() ENTT_NOEXCEPT { return service; } @@ -9913,7 +28303,7 @@ * * @return A reference to the service implementation currently set, if any. */ - static Service & ref() ENTT_NOEXCEPT { + [[nodiscard]] static Service & ref() ENTT_NOEXCEPT { return *service; } @@ -9933,7 +28323,7 @@ * @param ptr Service to use to replace the current one. */ static void set(std::shared_ptr ptr) { - ENTT_ASSERT(static_cast(ptr)); + ENTT_ASSERT(static_cast(ptr), "Null service not allowed"); service = std::move(ptr); } @@ -9954,127 +28344,828 @@ } -#endif // ENTT_LOCATOR_LOCATOR_HPP - -// #include "meta/factory.hpp" -#ifndef ENTT_META_FACTORY_HPP -#define ENTT_META_FACTORY_HPP - - -#include +#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 +#include +#include +#include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC +#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 // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - -// #include "policy.hpp" -#ifndef ENTT_META_POLICY_HPP -#define ENTT_META_POLICY_HPP - - -namespace entt { - - -/*! @brief Empty class type used to request the _as alias_ policy. */ -struct as_alias_t {}; - - -/*! @brief Disambiguation tag. */ -constexpr as_alias_t as_alias; - - -/*! @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 // ENTT_META_POLICY_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 "policy.hpp" - - - -namespace entt { - - -class meta_any; -struct meta_handle; -class meta_prop; -class meta_base; -class meta_conv; -class meta_ctor; -class meta_dtor; -class meta_data; -class meta_func; -class meta_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; + + +/*! @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; /** @@ -10086,2087 +29177,649 @@ namespace internal { -struct meta_type_node; - - -struct meta_prop_node { - meta_prop_node * next; - meta_any(* const key)(); - meta_any(* const value)(); - meta_prop(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_base_node { - meta_base_node ** const underlying; - meta_type_node * const parent; - meta_base_node * next; - meta_type_node *(* const type)() ENTT_NOEXCEPT; - void *(* const cast)(void *) ENTT_NOEXCEPT; - meta_base(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_conv_node { - meta_conv_node ** const underlying; - meta_type_node * const parent; - meta_conv_node * next; - meta_type_node *(* const type)() ENTT_NOEXCEPT; - meta_any(* const conv)(const void *); - meta_conv(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_ctor_node { - using size_type = std::size_t; - meta_ctor_node ** const underlying; - meta_type_node * const parent; - meta_ctor_node * next; - meta_prop_node * prop; - const size_type size; - meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; - meta_any(* const invoke)(meta_any * const); - meta_ctor(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_dtor_node { - meta_dtor_node ** const underlying; - meta_type_node * const parent; - bool(* const invoke)(meta_handle); - meta_dtor(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_data_node { - meta_data_node ** const underlying; - ENTT_ID_TYPE identifier; - 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); - meta_any(* const get)(meta_handle, meta_any); - meta_data(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_func_node { - using size_type = std::size_t; - meta_func_node ** const underlying; - ENTT_ID_TYPE identifier; - meta_type_node * const parent; - meta_func_node * next; - meta_prop_node * prop; - const size_type size; - const bool is_const; - const bool is_static; - meta_type_node *(* const ret)() ENTT_NOEXCEPT; - meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; - meta_any(* const invoke)(meta_handle, meta_any *); - meta_func(* const meta)() ENTT_NOEXCEPT; -}; - - -struct meta_type_node { - using size_type = std::size_t; - ENTT_ID_TYPE identifier; - meta_type_node * next; - meta_prop_node * prop; - 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; - const bool is_member_object_pointer; - const bool is_member_function_pointer; - const size_type extent; - meta_type(* const remove_pointer)() ENTT_NOEXCEPT; - meta_type(* const meta)() ENTT_NOEXCEPT; - meta_base_node *base{nullptr}; - meta_conv_node *conv{nullptr}; - meta_ctor_node *ctor{nullptr}; - meta_dtor_node *dtor{nullptr}; - meta_data_node *data{nullptr}; - meta_func_node *func{nullptr}; -}; - - -template -struct meta_node { - inline static meta_type_node *type = nullptr; -}; - - -template -struct meta_node { - inline static meta_type_node *type = nullptr; - - template - inline static meta_base_node *base = nullptr; - - template - inline static meta_conv_node *conv = nullptr; - - template - inline static meta_ctor_node *ctor = nullptr; - - template - inline static meta_dtor_node *dtor = nullptr; - - template - inline static meta_data_node *data = nullptr; - - template - inline static meta_func_node *func = nullptr; - - inline static meta_type_node * resolve() ENTT_NOEXCEPT; -}; - - -template -struct meta_info: meta_node>...> {}; - - -template -void iterate(Op op, const Node *curr) ENTT_NOEXCEPT { - while(curr) { - op(curr); - curr = curr->next; - } -} - - -template -void iterate(Op op, const meta_type_node *node) ENTT_NOEXCEPT { - if(node) { - auto *curr = node->base; - iterate(op, node->*Member); - - while(curr) { - iterate(op, curr->type()); - curr = curr->next; - } - } -} - - -template -auto find_if(Op op, const Node *curr) ENTT_NOEXCEPT { - while(curr && !op(curr)) { - curr = curr->next; - } - - return curr; -} - - -template -auto find_if(Op op, const meta_type_node *node) ENTT_NOEXCEPT --> decltype(find_if(op, node->*Member)) { - decltype(find_if(op, node->*Member)) ret = nullptr; - - if(node) { - ret = find_if(op, node->*Member); - auto *curr = node->base; - - while(curr && !ret) { - ret = find_if(op, curr->type()); - curr = curr->next; - } - } - - return ret; -} - - -template -const Type * try_cast(const meta_type_node *node, void *instance) ENTT_NOEXCEPT { - const auto *type = meta_info::resolve(); - void *ret = nullptr; - - if(node == type) { - ret = instance; +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(is_iterator_v) { + return true; + } else if constexpr(std::is_same_v) { + return is_equality_comparable(choice<0>); } else { - const auto *base = find_if<&meta_type_node::base>([type](auto *candidate) { - return candidate->type() == type; - }, node); - - ret = base ? base->cast(instance) : nullptr; - } - - return static_cast(ret); -} - - -template -inline bool can_cast_or_convert(const meta_type_node *from, const meta_type_node *to) ENTT_NOEXCEPT { - return (from == to) || find_if([to](auto *node) { - return node->type() == to; - }, from); -} - - -template -inline auto ctor(std::index_sequence, const meta_type_node *node) ENTT_NOEXCEPT { - return internal::find_if([](auto *candidate) { - return candidate->size == sizeof...(Args) && - (([](auto *from, auto *to) { - return internal::can_cast_or_convert<&internal::meta_type_node::base>(from, to) - || internal::can_cast_or_convert<&internal::meta_type_node::conv>(from, to); - }(internal::meta_info::resolve(), candidate->arg(Indexes))) && ...); - }, node->ctor); -} - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/** - * @brief Meta any object. - * - * A meta any is an opaque container for single values of any type. - * - * This class uses a technique called small buffer optimization (SBO) to - * completely eliminate the need to allocate memory, where possible.
- * From the user's point of view, nothing will change, but the elimination of - * allocations will reduce the jumps in memory and therefore will avoid chasing - * of pointers. This will greatly improve the use of the cache, thus increasing - * the overall performance. - */ -class meta_any { - /*! @brief A meta handle is allowed to _inherit_ from a meta any. */ - friend struct meta_handle; - - using storage_type = std::aligned_storage_t; - using compare_fn_type = bool(const void *, const void *); - using copy_fn_type = void *(storage_type &, const void *); - using destroy_fn_type = void(void *); - using steal_fn_type = void *(storage_type &, void *, destroy_fn_type *); - - template> - struct type_traits { - template - static void * instance(storage_type &storage, Args &&... args) { - auto instance = std::make_unique(std::forward(args)...); - new (&storage) Type *{instance.get()}; - return instance.release(); - } - - static void destroy(void *instance) { - auto *node = internal::meta_info::resolve(); - auto *actual = static_cast(instance); - [[maybe_unused]] const bool destroyed = node->meta().destroy(*actual); - ENTT_ASSERT(destroyed); - delete actual; - } - - static void * copy(storage_type &storage, const void *other) { - auto instance = std::make_unique(*static_cast(other)); - new (&storage) Type *{instance.get()}; - return instance.release(); - } - - static void * steal(storage_type &to, void *from, destroy_fn_type *) { - auto *instance = static_cast(from); - new (&to) Type *{instance}; - return instance; - } - }; - - template - struct type_traits>> { - template - static void * instance(storage_type &storage, Args &&... args) { - return new (&storage) Type{std::forward(args)...}; - } - - static void destroy(void *instance) { - auto *node = internal::meta_info::resolve(); - auto *actual = static_cast(instance); - [[maybe_unused]] const bool destroyed = node->meta().destroy(*actual); - ENTT_ASSERT(destroyed); - actual->~Type(); - } - - static void * copy(storage_type &storage, const void *instance) { - return new (&storage) Type{*static_cast(instance)}; - } - - static void * steal(storage_type &to, void *from, destroy_fn_type *destroy_fn) { - void *instance = new (&to) Type{std::move(*static_cast(from))}; - destroy_fn(from); - return instance; - } - }; - - template - static auto compare(int, const Type &lhs, const Type &rhs) - -> decltype(lhs == rhs, bool{}) { - return lhs == rhs; - } - - template - static bool compare(char, const Type &lhs, const Type &rhs) { - return &lhs == &rhs; - } - -public: - /*! @brief Default constructor. */ - meta_any() ENTT_NOEXCEPT - : storage{}, - instance{nullptr}, - node{nullptr}, - destroy_fn{nullptr}, - compare_fn{nullptr}, - copy_fn{nullptr}, - steal_fn{nullptr} - {} - - /** - * @brief Constructs a meta any by directly initializing the new object. - * @tparam Type Type of object to use to initialize the container. - * @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, [[maybe_unused]] Args &&... args) - : meta_any{} - { - node = internal::meta_info::resolve(); - - if constexpr(!std::is_void_v) { - using traits_type = type_traits>>; - instance = traits_type::instance(storage, std::forward(args)...); - destroy_fn = &traits_type::destroy; - copy_fn = &traits_type::copy; - steal_fn = &traits_type::steal; - - compare_fn = [](const void *lhs, const void *rhs) { - return compare(0, *static_cast(lhs), *static_cast(rhs)); - }; - } - } - - /** - * @brief Constructs a meta any that holds an unmanaged object. - * @tparam Type Type of object to use to initialize the container. - * @param type An instance of an object to use to initialize the container. - */ - template - explicit meta_any(as_alias_t, Type &type) - : meta_any{} - { - node = internal::meta_info::resolve(); - instance = &type; - - compare_fn = [](const void *lhs, const void *rhs) { - return compare(0, *static_cast(lhs), *static_cast(rhs)); - }; - } - - /** - * @brief Constructs a meta any from a given value. - * @tparam Type Type of object to use to initialize the container. - * @param type An instance of an object to use to initialize the container. - */ - template>, meta_any>>> - meta_any(Type &&type) - : meta_any{std::in_place_type>>, std::forward(type)} - {} - - /** - * @brief Copy constructor. - * @param other The instance to copy from. - */ - meta_any(const meta_any &other) - : meta_any{} - { - node = other.node; - instance = other.copy_fn ? other.copy_fn(storage, other.instance) : other.instance; - destroy_fn = other.destroy_fn; - compare_fn = other.compare_fn; - copy_fn = other.copy_fn; - steal_fn = other.steal_fn; - } - - /** - * @brief Move constructor. - * - * After meta any move construction, instances that have been moved from - * are placed in a valid but unspecified state. It's highly discouraged to - * continue using them. - * - * @param other The instance to move from. - */ - meta_any(meta_any &&other) ENTT_NOEXCEPT - : meta_any{} - { - swap(*this, other); - } - - /*! @brief Frees the internal storage, whatever it means. */ - ~meta_any() { - if(destroy_fn) { - destroy_fn(instance); - } - } - - /** - * @brief Assignment operator. - * @tparam Type Type of object to use to initialize the container. - * @param type An instance of an object to use to initialize the container. - * @return This meta any object. - */ - template>, meta_any>>> - meta_any & operator=(Type &&type) { - return (*this = meta_any{std::forward(type)}); - } - - /** - * @brief Copy assignment operator. - * @param other The instance to assign. - * @return This meta any object. - */ - meta_any & operator=(const meta_any &other) { - return (*this = meta_any{other}); - } - - /** - * @brief Move assignment operator. - * @param other The instance to assign. - * @return This meta any object. - */ - meta_any & operator=(meta_any &&other) ENTT_NOEXCEPT { - meta_any any{std::move(other)}; - swap(any, *this); - return *this; - } - - /** - * @brief Returns the meta type of the underlying object. - * @return The meta type of the underlying object, if any. - */ - 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. - */ - const void * data() const ENTT_NOEXCEPT { - return instance; - } - - /*! @copydoc data */ - void * data() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).data()); - } - - /** - * @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 - const Type * try_cast() const ENTT_NOEXCEPT { - return internal::try_cast(node, instance); - } - - /*! @copydoc try_cast */ - template - Type * try_cast() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).try_cast()); - } - - /** - * @brief Tries to cast an instance to a given type. - * - * The type of the instance must be such that the cast is possible. - * - * @warning - * Attempting to perform a cast that isn't viable results in undefined - * behavior.
- * An assertion will abort the execution at runtime in debug mode in case - * the cast is not feasible. - * - * @tparam Type Type to which to cast the instance. - * @return A reference to the contained instance. - */ - template - const Type & cast() const ENTT_NOEXCEPT { - auto *actual = try_cast(); - ENTT_ASSERT(actual); - return *actual; - } - - /*! @copydoc cast */ - template - Type & cast() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).cast()); - } - - /** - * @brief Tries to convert an instance to a given type and returns it. - * @tparam Type Type to which to convert the instance. - * @return A valid meta any object if the conversion is possible, an invalid - * one otherwise. - */ - template - meta_any convert() const { - meta_any any{}; - - if(const auto *type = internal::meta_info::resolve(); node == type) { - any = *static_cast(instance); - } else { - const auto *conv = internal::find_if<&internal::meta_type_node::conv>([type](auto *other) { - return other->type() == type; - }, node); - - if(conv) { - any = conv->conv(instance); - } - } - - return any; - } - - /** - * @brief Tries to convert an instance to a given type. - * @tparam Type Type to which to convert the instance. - * @return True if the conversion is possible, false otherwise. - */ - template - bool convert() { - bool valid = (node == internal::meta_info::resolve()); - - if(!valid) { - if(auto any = std::as_const(*this).convert(); any) { - swap(any, *this); - valid = true; - } - } - - return valid; - } - - /** - * @brief Replaces the contained object by initializing a new instance - * directly. - * @tparam Type Type of object to use to initialize the container. - * @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 = meta_any{std::in_place_type_t{}, std::forward(args)...}; - } - - /** - * @brief Returns false if a container is empty, true otherwise. - * @return False if the container is empty, true otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two containers differ in their content. - * @param other Container with which to compare. - * @return False if the two containers differ in their content, true - * otherwise. - */ - bool operator==(const meta_any &other) const ENTT_NOEXCEPT { - return node == other.node && ((!compare_fn && !other.compare_fn) || compare_fn(instance, other.instance)); - } - - /** - * @brief Swaps two meta any objects. - * @param lhs A valid meta any object. - * @param rhs A valid meta any object. - */ - friend void swap(meta_any &lhs, meta_any &rhs) ENTT_NOEXCEPT { - if(lhs.steal_fn && rhs.steal_fn) { - storage_type buffer; - auto *temp = lhs.steal_fn(buffer, lhs.instance, lhs.destroy_fn); - lhs.instance = rhs.steal_fn(lhs.storage, rhs.instance, rhs.destroy_fn); - rhs.instance = lhs.steal_fn(rhs.storage, temp, lhs.destroy_fn); - } else if(lhs.steal_fn) { - lhs.instance = lhs.steal_fn(rhs.storage, lhs.instance, lhs.destroy_fn); - std::swap(rhs.instance, lhs.instance); - } else if(rhs.steal_fn) { - rhs.instance = rhs.steal_fn(lhs.storage, rhs.instance, rhs.destroy_fn); - std::swap(rhs.instance, lhs.instance); - } else { - std::swap(lhs.instance, rhs.instance); - } - - std::swap(lhs.node, rhs.node); - std::swap(lhs.destroy_fn, rhs.destroy_fn); - std::swap(lhs.compare_fn, rhs.compare_fn); - std::swap(lhs.copy_fn, rhs.copy_fn); - std::swap(lhs.steal_fn, rhs.steal_fn); - } - -private: - storage_type storage; - void *instance; - internal::meta_type_node *node; - destroy_fn_type *destroy_fn; - compare_fn_type *compare_fn; - copy_fn_type *copy_fn; - steal_fn_type *steal_fn; -}; - - -/** - * @brief Meta handle object. - * - * A meta handle is an opaque pointer to an instance 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. Users are - * responsible for ensuring that the target object remains alive for the entire - * interval of use of the handle. - */ -struct meta_handle { - /*! @brief Default constructor. */ - meta_handle() ENTT_NOEXCEPT - : node{nullptr}, - instance{nullptr} - {} - - /** - * @brief Constructs a meta handle from a meta any object. - * @param any A reference to an object to use to initialize the handle. - */ - meta_handle(meta_any &any) ENTT_NOEXCEPT - : node{any.node}, - instance{any.instance} - {} - - /** - * @brief Constructs a meta handle from a given instance. - * @tparam Type Type of object to use to initialize the handle. - * @param obj A reference to an object to use to initialize the handle. - */ - template>, meta_handle>>> - meta_handle(Type &obj) ENTT_NOEXCEPT - : node{internal::meta_info::resolve()}, - instance{&obj} - {} - - /** - * @brief Returns the meta type of the underlying object. - * @return The meta type of the underlying object, if any. - */ - 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. - */ - const void * data() const ENTT_NOEXCEPT { - return instance; - } - - /*! @copydoc data */ - void * data() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).data()); - } - - /** - * @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 underlying object. - */ - template - const Type * data() const ENTT_NOEXCEPT { - return internal::try_cast(node, instance); - } - - /*! @copydoc data */ - template - Type * data() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).data()); - } - - /** - * @brief Returns false if a handle is empty, true otherwise. - * @return False if the handle is empty, true otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return instance; - } - -private: - const internal::meta_type_node *node; - void *instance; -}; - - -/** - * @brief Checks if two containers differ in their content. - * @param lhs A meta any object, either empty or not. - * @param rhs A meta any object, either empty or not. - * @return True if the two containers differ in their content, false otherwise. - */ -inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta property object. - * - * A meta property is an opaque container for a key/value pair.
- * Properties are associated with any other meta object to enrich it. - */ -class meta_prop { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_prop(const internal::meta_prop_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Default constructor. */ - meta_prop() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the stored key. - * @return A meta any containing the key stored with the given property. - */ - meta_any key() const ENTT_NOEXCEPT { - return node->key(); - } - - /** - * @brief Returns the stored value. - * @return A meta any containing the value stored with the given property. - */ - meta_any value() const ENTT_NOEXCEPT { - return node->value(); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_prop &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_prop_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta base object. - * - * A meta base is an opaque container for a base class to be used to walk - * through hierarchies. - */ -class meta_base { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_base(const internal::meta_base_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Default constructor. */ - meta_base() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the meta type to which a meta base belongs. - * @return The meta type to which the meta base belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Returns the meta type of a given meta base. - * @return The meta type of the meta base. - */ - inline meta_type type() const ENTT_NOEXCEPT; - - /** - * @brief Casts an instance from a parent type to a base type. - * @param instance The instance to cast. - * @return An opaque pointer to the base type. - */ - void * cast(void *instance) const ENTT_NOEXCEPT { - return node->cast(instance); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_base &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_base_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_base &lhs, const meta_base &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta conversion function object. - * - * A meta conversion function is an opaque container for a conversion function - * to be used to convert a given instance to another type. - */ -class meta_conv { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_conv(const internal::meta_conv_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Default constructor. */ - meta_conv() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the meta type to which a meta conversion function belongs. - * @return The meta type to which the meta conversion function belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Returns the meta type of a given meta conversion function. - * @return The meta type of the meta conversion function. - */ - inline meta_type type() const ENTT_NOEXCEPT; - - /** - * @brief Converts an instance to a given type. - * @param instance The instance to convert. - * @return An opaque pointer to the instance to convert. - */ - meta_any convert(const void *instance) const ENTT_NOEXCEPT { - return node->conv(instance); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_conv &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_conv_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_conv &lhs, const meta_conv &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta constructor object. - * - * A meta constructor is an opaque container for a function to be used to - * construct instances of a given type. - */ -class meta_ctor { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_ctor(const internal::meta_ctor_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Unsigned integer type. */ - using size_type = typename internal::meta_ctor_node::size_type; - - /*! @brief Default constructor. */ - meta_ctor() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the meta type to which a meta constructor belongs. - * @return The meta type to which the meta constructor belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Returns the number of arguments accepted by a meta constructor. - * @return The number of arguments accepted by the meta constructor. - */ - size_type size() const ENTT_NOEXCEPT { - return node->size; - } - - /** - * @brief Returns the meta type of the i-th argument of a meta constructor. - * @param index The index of the argument of which to return the meta type. - * @return The meta type of the i-th argument of a meta constructor, if any. - */ - meta_type arg(size_type index) const ENTT_NOEXCEPT; - - /** - * @brief Creates an instance of the underlying type, if possible. - * - * To create a valid instance, the types of the parameters must coincide - * exactly with those required by the underlying meta constructor. - * Otherwise, an empty and then invalid container is returned. - * - * @tparam Args Types of arguments to use to construct the instance. - * @param args Parameters to use to construct the instance. - * @return A meta any containing the new instance, if any. - */ - template - meta_any invoke(Args &&... args) const { - std::array arguments{{std::forward(args)...}}; - meta_any any{}; - - if(sizeof...(Args) == size()) { - any = node->invoke(arguments.data()); - } - - return any; - } - - /** - * @brief Iterates all the properties assigned to a meta constructor. - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - prop(Op op) const ENTT_NOEXCEPT { - internal::iterate([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node->prop); - } - - /** - * @brief Returns the property associated with a given key. - * @tparam Key Type of key to use to search for a property. - * @param key The key to use to search for a property. - * @return The property associated with the given key, if any. - */ - template - std::enable_if_t, meta_prop> - prop(Key &&key) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if([key = meta_any{std::forward(key)}](auto *candidate) { - return candidate->key() == key; - }, node->prop); - - return curr ? curr->meta() : meta_prop{}; - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_ctor &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_ctor_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_ctor &lhs, const meta_ctor &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta destructor object. - * - * A meta destructor is an opaque container for a function to be used to - * destroy instances of a given type. - */ -class meta_dtor { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_dtor(const internal::meta_dtor_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Default constructor. */ - meta_dtor() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the meta type to which a meta destructor belongs. - * @return The meta type to which the meta destructor belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Destroys an instance of the underlying type. - * - * It must be possible to cast the instance to the parent type of the meta - * destructor. Otherwise, invoking the meta destructor results in an - * undefined behavior. - * - * @param handle An opaque pointer to an instance of the underlying type. + 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 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. */ - bool invoke(meta_handle handle) const { - return node->invoke(handle); - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_dtor &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_dtor_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_dtor &lhs, const meta_dtor &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta data object. - * - * A meta data is an opaque container for a data member associated with a given - * type. - */ -class meta_data { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_data(const internal::meta_data_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Default constructor. */ - meta_data() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the identifier assigned to a given meta data. - * @return The identifier assigned to the meta data. - */ - ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT { - return node->identifier; - } - - /** - * @brief Returns the meta type to which a meta data belongs. - * @return The meta type to which the meta data belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Indicates whether a given meta data is constant or not. - * @return True if the meta data is constant, false otherwise. - */ - bool is_const() const ENTT_NOEXCEPT { - return node->is_const; - } - - /** - * @brief Indicates whether a given meta data is static or not. - * - * A static meta data is such that it can be accessed using a null pointer - * as an instance. - * - * @return True if the meta data is static, false otherwise. - */ - bool is_static() const ENTT_NOEXCEPT { - return node->is_static; - } - - /** - * @brief Returns the meta type of a given meta data. - * @return The meta type of the meta data. - */ - inline meta_type type() const ENTT_NOEXCEPT; - - /** - * @brief Sets the value of the variable enclosed by a given meta type. - * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the setter results in an undefined - * behavior.
- * The type of the value must coincide exactly with that of the variable - * enclosed by the meta data. Otherwise, invoking the setter does nothing. - * - * @tparam Type Type of value to assign. - * @param handle An opaque pointer to an 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 handle, Type &&value) const { - return node->set(handle, meta_any{}, std::forward(value)); - } - - /** - * @brief Sets the i-th element of an array enclosed by a given meta type. - * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the setter results in an undefined - * behavior.
- * The type of the value must coincide exactly with that of the array type - * enclosed by the meta data. Otherwise, invoking the setter does nothing. - * - * @tparam Type Type of value to assign. - * @param handle An opaque pointer to an instance of the underlying type. - * @param index Position of the underlying element to set. - * @param value Parameter to use to set the underlying element. + [[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. */ - template - bool set(meta_handle handle, std::size_t index, Type &&value) const { - ENTT_ASSERT(index < node->type()->extent); - return node->set(handle, index, std::forward(value)); - } - - /** - * @brief Gets the value of the variable enclosed by a given meta type. - * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the getter results in an undefined behavior. - * - * @param handle An opaque pointer to an instance of the underlying type. - * @return A meta any containing the value of the underlying variable. - */ - meta_any get(meta_handle handle) const ENTT_NOEXCEPT { - return node->get(handle, meta_any{}); - } - - /** - * @brief Gets the i-th element of an array enclosed by a given meta type. - * - * It must be possible to cast the instance to the parent type of the meta - * data. Otherwise, invoking the getter results in an undefined behavior. - * - * @param handle An opaque pointer to an instance of the underlying type. - * @param index Position of the underlying element to get. - * @return A meta any containing the value of the underlying element. - */ - meta_any get(meta_handle handle, std::size_t index) const ENTT_NOEXCEPT { - ENTT_ASSERT(index < node->type()->extent); - return node->get(handle, index); - } - - /** - * @brief Iterates all the properties assigned to a meta data. - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - prop(Op op) const ENTT_NOEXCEPT { - internal::iterate([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node->prop); - } - - /** - * @brief Returns the property associated with a given key. - * @tparam Key Type of key to use to search for a property. - * @param key The key to use to search for a property. - * @return The property associated with the given key, if any. - */ - template - std::enable_if_t, meta_prop> - prop(Key &&key) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if([key = meta_any{std::forward(key)}](auto *candidate) { - return candidate->key() == key; - }, node->prop); - - return curr ? curr->meta() : meta_prop{}; - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_data &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_data_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_data &lhs, const meta_data &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta function object. - * - * A meta function is an opaque container for a member function associated with - * a given type. - */ -class meta_func { - /*! @brief A meta factory is allowed to create meta objects. */ - template friend class meta_factory; - - meta_func(const internal::meta_func_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Unsigned integer type. */ - using size_type = typename internal::meta_func_node::size_type; - - /*! @brief Default constructor. */ - meta_func() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the identifier assigned to a given meta function. - * @return The identifier assigned to the meta function. - */ - ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT { - return node->identifier; - } - - /** - * @brief Returns the meta type to which a meta function belongs. - * @return The meta type to which the meta function belongs. - */ - inline meta_type parent() const ENTT_NOEXCEPT; - - /** - * @brief Returns the number of arguments accepted by a meta function. - * @return The number of arguments accepted by the meta function. - */ - size_type size() const ENTT_NOEXCEPT { - return node->size; - } - - /** - * @brief Indicates whether a given meta function is constant or not. - * @return True if the meta function is constant, false otherwise. - */ - bool is_const() const ENTT_NOEXCEPT { - return node->is_const; - } - - /** - * @brief Indicates whether a given meta function is static or not. - * - * A static meta function is such that it can be invoked using a null - * pointer as an instance. - * - * @return True if the meta function is static, false otherwise. - */ - bool is_static() const ENTT_NOEXCEPT { - return node->is_static; - } - - /** - * @brief Returns the meta type of the return type of a meta function. - * @return The meta type of the return type of the meta function. - */ - inline meta_type ret() const ENTT_NOEXCEPT; - - /** - * @brief Returns the meta type of the i-th argument of a meta function. - * @param index The index of the argument of which to return the meta type. - * @return The meta type of the i-th argument of a meta function, if any. - */ - inline meta_type arg(size_type index) const ENTT_NOEXCEPT; - - /** - * @brief Invokes the underlying function, if possible. - * - * To invoke a meta function, the types of the parameters must coincide - * exactly with those required by the underlying function. Otherwise, an - * empty and then invalid container is returned.
- * It must be possible to cast the instance to the parent type of the meta - * function. Otherwise, invoking the underlying function results in an - * undefined behavior. - * - * @tparam Args Types of arguments to use to invoke the function. - * @param handle An opaque pointer to an instance of the underlying type. - * @param args Parameters to use to invoke the function. - * @return A meta any containing the returned value, if any. - */ - template - meta_any invoke(meta_handle handle, Args &&... args) const { - std::array arguments{{std::forward(args)...}}; - meta_any any{}; - - if(sizeof...(Args) == size()) { - any = node->invoke(handle, arguments.data()); - } - - return any; - } - - /** - * @brief Iterates all the properties assigned to a meta function. - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - prop(Op op) const ENTT_NOEXCEPT { - internal::iterate([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node->prop); - } - - /** - * @brief Returns the property associated with a given key. - * @tparam Key Type of key to use to search for a property. - * @param key The key to use to search for a property. - * @return The property associated with the given key, if any. - */ - template - std::enable_if_t, meta_prop> - prop(Key &&key) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if([key = meta_any{std::forward(key)}](auto *candidate) { - return candidate->key() == key; - }, node->prop); - - return curr ? curr->meta() : meta_prop{}; - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_func &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_func_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_func &lhs, const meta_func &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Meta type object. - * - * A meta type is the starting point for accessing a reflected type, thus being - * able to work through it on real objects. - */ -class meta_type { - /*! @brief A meta node is allowed to create meta objects. */ - template friend struct internal::meta_node; - - meta_type(const internal::meta_type_node *curr) ENTT_NOEXCEPT - : node{curr} - {} - -public: - /*! @brief Unsigned integer type. */ - using size_type = typename internal::meta_type_node::size_type; - - /*! @brief Default constructor. */ - meta_type() ENTT_NOEXCEPT - : node{nullptr} - {} - - /** - * @brief Returns the identifier assigned to a given meta type. - * @return The identifier assigned to the meta type. - */ - ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT { - return node->identifier; - } - - /** - * @brief Indicates whether a given meta type refers to void or not. - * @return True if the underlying type is void, false otherwise. - */ - bool is_void() const ENTT_NOEXCEPT { - return node->is_void; - } - - /** - * @brief Indicates whether a given meta type refers to an integral type or - * not. - * @return True if the underlying type is an integral type, false otherwise. - */ - bool is_integral() const ENTT_NOEXCEPT { - return node->is_integral; - } - - /** - * @brief Indicates whether a given meta type refers to a floating-point - * type or not. - * @return True if the underlying type is a floating-point type, false - * otherwise. - */ - bool is_floating_point() const ENTT_NOEXCEPT { - return node->is_floating_point; - } - - /** - * @brief Indicates whether a given meta type refers to an array type or - * not. - * @return True if the underlying type is an array type, false otherwise. - */ - bool is_array() const ENTT_NOEXCEPT { - return node->is_array; - } - - /** - * @brief Indicates whether a given meta type refers to an enum or not. - * @return True if the underlying type is an enum, false otherwise. - */ - bool is_enum() const ENTT_NOEXCEPT { - return node->is_enum; - } - - /** - * @brief Indicates whether a given meta type refers to an union or not. - * @return True if the underlying type is an union, false otherwise. - */ - bool is_union() const ENTT_NOEXCEPT { - return node->is_union; - } - - /** - * @brief Indicates whether a given meta type refers to a class or not. - * @return True if the underlying type is a class, false otherwise. - */ - bool is_class() const ENTT_NOEXCEPT { - return node->is_class; - } - - /** - * @brief Indicates whether a given meta type refers to a pointer or not. - * @return True if the underlying type is a pointer, false otherwise. - */ - bool is_pointer() const ENTT_NOEXCEPT { - return node->is_pointer; - } - - /** - * @brief Indicates whether a given meta type refers to a function type or - * not. - * @return True if the underlying type is a function, false otherwise. - */ - bool is_function() const ENTT_NOEXCEPT { - return node->is_function; - } - - /** - * @brief Indicates whether a given meta type refers to a pointer to data - * member or not. - * @return True if the underlying type is a pointer to data member, false - * otherwise. - */ - bool is_member_object_pointer() const ENTT_NOEXCEPT { - return node->is_member_object_pointer; - } - - /** - * @brief Indicates whether a given meta type refers to a pointer to member - * function or not. - * @return True if the underlying type is a pointer to member function, - * false otherwise. - */ - bool is_member_function_pointer() const ENTT_NOEXCEPT { - return node->is_member_function_pointer; - } - - /** - * @brief If a given meta type refers to an array type, provides the number - * of elements of the array. - * @return The number of elements of the array if the underlying type is an - * array type, 0 otherwise. - */ - size_type extent() const ENTT_NOEXCEPT { - return node->extent; - } - - /** - * @brief Provides the meta type for which the pointer is defined. - * @return The meta type for which the pointer is defined or this meta type - * if it doesn't refer to a pointer type. - */ - meta_type remove_pointer() const ENTT_NOEXCEPT { - return node->remove_pointer(); - } - - /** - * @brief Iterates all the meta base of a meta type. - * - * Iteratively returns **all** the base classes of the given type. - * - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - base(Op op) const ENTT_NOEXCEPT { - internal::iterate<&internal::meta_type_node::base>([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node); - } - - /** - * @brief Returns the meta base associated with a given identifier. - * - * Searches recursively among **all** the base classes of the given type. - * - * @param identifier Unique identifier. - * @return The meta base associated with the given identifier, if any. - */ - meta_base base(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if<&internal::meta_type_node::base>([identifier](auto *candidate) { - return candidate->type()->identifier == identifier; - }, node); - - return curr ? curr->meta() : meta_base{}; - } - - /** - * @brief Iterates all the meta conversion functions of a meta type. - * - * Iteratively returns **all** the meta conversion functions of the given - * type. - * - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - void conv(Op op) const ENTT_NOEXCEPT { - internal::iterate<&internal::meta_type_node::conv>([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node); - } - - /** - * @brief Returns the meta conversion function associated with a given type. - * - * Searches recursively among **all** the conversion functions of the given - * type. - * - * @tparam Type The type to use to search for a meta conversion function. - * @return The meta conversion function associated with the given type, if - * any. - */ - template - meta_conv conv() const ENTT_NOEXCEPT { - const auto *curr = internal::find_if<&internal::meta_type_node::conv>([type = internal::meta_info::resolve()](auto *candidate) { - return candidate->type() == type; - }, node); - - return curr ? curr->meta() : meta_conv{}; - } - - /** - * @brief Iterates all the meta constructors of a meta type. - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - void ctor(Op op) const ENTT_NOEXCEPT { - internal::iterate([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node->ctor); - } - - /** - * @brief Returns the meta constructor that accepts a given list of types of - * arguments. - * @return The requested meta constructor, if any. - */ - template - meta_ctor ctor() const ENTT_NOEXCEPT { - const auto *curr = internal::ctor(std::make_index_sequence{}, node); - return curr ? curr->meta() : meta_ctor{}; - } - - /** - * @brief Returns the meta destructor associated with a given type. - * @return The meta destructor associated with the given type, if any. - */ - meta_dtor dtor() const ENTT_NOEXCEPT { - return node->dtor ? node->dtor->meta() : meta_dtor{}; - } - - /** - * @brief Iterates all the meta data of a meta type. - * - * Iteratively returns **all** the meta data of the given type. This means - * that the meta data of the base classes will also be returned, if any. - * - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - data(Op op) const ENTT_NOEXCEPT { - internal::iterate<&internal::meta_type_node::data>([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node); - } - - /** - * @brief Returns the meta data associated with a given identifier. - * - * Searches recursively among **all** the meta data of the given type. This - * means that the meta data of the base classes will also be inspected, if - * any. - * - * @param identifier Unique identifier. - * @return The meta data associated with the given identifier, if any. - */ - meta_data data(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if<&internal::meta_type_node::data>([identifier](auto *candidate) { - return candidate->identifier == identifier; - }, node); - - return curr ? curr->meta() : meta_data{}; - } - - /** - * @brief Iterates all the meta functions of a meta type. - * - * Iteratively returns **all** the meta functions of the given type. This - * means that the meta functions of the base classes will also be returned, - * if any. - * - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - func(Op op) const ENTT_NOEXCEPT { - internal::iterate<&internal::meta_type_node::func>([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node); - } - - /** - * @brief Returns the meta function associated with a given identifier. - * - * Searches recursively among **all** the meta functions of the given type. - * This means that the meta functions of the base classes will also be - * inspected, if any. - * - * @param identifier Unique identifier. - * @return The meta function associated with the given identifier, if any. - */ - meta_func func(const ENTT_ID_TYPE identifier) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if<&internal::meta_type_node::func>([identifier](auto *candidate) { - return candidate->identifier == identifier; - }, node); - - return curr ? curr->meta() : meta_func{}; - } - - /** - * @brief Creates an instance of the underlying type, if possible. - * - * To create a valid instance, the types of the parameters must coincide - * exactly with those required by the underlying meta constructor. - * Otherwise, an empty and then invalid container is returned. - * - * @tparam Args Types of arguments to use to construct the instance. - * @param args Parameters to use to construct the instance. - * @return A meta any containing the new instance, if any. - */ - template - meta_any construct(Args &&... args) const { - std::array arguments{{std::forward(args)...}}; - meta_any any{}; - - internal::find_if<&internal::meta_type_node::ctor>([data = arguments.data(), &any](auto *curr) -> bool { - if(curr->size == sizeof...(args)) { - any = curr->invoke(data); - } - - return static_cast(any); - }, node); - - return any; - } - - /** - * @brief Destroys an instance of the underlying type. - * - * It must be possible to cast the instance to the underlying type. - * Otherwise, invoking the meta destructor results in an undefined - * behavior.
- * If no destructor has been set, this function returns true without doing - * anything. - * - * @param handle An opaque pointer to an instance of the underlying type. - * @return True in case of success, false otherwise. - */ - bool destroy(meta_handle handle) const { - return (handle.type() == node->meta()) && (!node->dtor || node->dtor->invoke(handle)); - } - - /** - * @brief Iterates all the properties assigned to a meta type. - * - * Iteratively returns **all** the properties of the given type. This means - * that the properties of the base classes will also be returned, if any. - * - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ - template - std::enable_if_t, void> - prop(Op op) const ENTT_NOEXCEPT { - internal::iterate<&internal::meta_type_node::prop>([op = std::move(op)](auto *curr) { - op(curr->meta()); - }, node); - } - - /** - * @brief Returns the property associated with a given key. - * - * Searches recursively among **all** the properties of the given type. This - * means that the properties of the base classes will also be inspected, if - * any. - * - * @tparam Key Type of key to use to search for a property. - * @param key The key to use to search for a property. - * @return The property associated with the given key, if any. - */ - template - std::enable_if_t, meta_prop> - prop(Key &&key) const ENTT_NOEXCEPT { - const auto *curr = internal::find_if<&internal::meta_type_node::prop>([key = meta_any{std::forward(key)}](auto *candidate) { - return candidate->key() == key; - }, node); - - return curr ? curr->meta() : meta_prop{}; - } - - /** - * @brief Returns true if a meta object is valid, false otherwise. - * @return True if the meta object is valid, false otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - return node; - } - - /** - * @brief Checks if two meta objects refer to the same node. - * @param other The meta object with which to compare. - * @return True if the two meta objects refer to the same node, false - * otherwise. - */ - bool operator==(const meta_type &other) const ENTT_NOEXCEPT { - return node == other.node; - } - -private: - const internal::meta_type_node *node; -}; - - -/** - * @brief Checks if two meta objects refer to the same node. - * @param lhs A meta object, either valid or not. - * @param rhs A meta object, either valid or not. - * @return True if the two meta objects refer to the same node, false otherwise. - */ -inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -inline meta_type meta_any::type() const ENTT_NOEXCEPT { - return node ? node->meta() : meta_type{}; -} - - -inline meta_type meta_handle::type() const ENTT_NOEXCEPT { - return node ? node->meta() : meta_type{}; -} - - -inline meta_type meta_base::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_base::type() const ENTT_NOEXCEPT { - return node->type()->meta(); -} - - -inline meta_type meta_conv::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_conv::type() const ENTT_NOEXCEPT { - return node->type()->meta(); -} - - -inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index)->meta() : meta_type{}; -} - - -inline meta_type meta_dtor::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_data::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_data::type() const ENTT_NOEXCEPT { - return node->type()->meta(); -} - - -inline meta_type meta_func::parent() const ENTT_NOEXCEPT { - return node->parent->meta(); -} - - -inline meta_type meta_func::ret() const ENTT_NOEXCEPT { - return node->ret()->meta(); -} - - -inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { - return index < size() ? node->arg(index)->meta() : meta_type{}; -} - - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -namespace internal { - - -template -inline meta_type_node * meta_node::resolve() ENTT_NOEXCEPT { - if(!type) { - static meta_type_node node{ - {}, - nullptr, - nullptr, - 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_function_v, - std::is_member_object_pointer_v, - std::is_member_function_pointer_v, - std::extent_v, - []() ENTT_NOEXCEPT -> meta_type { - return internal::meta_info>::resolve(); - }, - []() ENTT_NOEXCEPT -> meta_type { - return &node; - } - }; - - type = &node; - } - - return type; -} - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -} - - -#endif // ENTT_META_META_HPP + [[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" @@ -12182,2626 +29835,150 @@ namespace internal { -template -struct meta_function_helper; - - -template -struct meta_function_helper { - using return_type = std::remove_cv_t>; - using args_type = std::tuple>...>; - - static constexpr auto size = sizeof...(Args); - static constexpr auto is_const = false; - - static auto arg(typename internal::meta_func_node::size_type index) ENTT_NOEXCEPT { - return std::array{{meta_info::resolve()...}}[index]; - } -}; - - -template -struct meta_function_helper: meta_function_helper { - static constexpr auto is_const = true; -}; - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(Class:: *)(Args...)); - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(Class:: *)(Args...) const); - - -template -constexpr meta_function_helper -to_meta_function_helper(Ret(*)(Args...)); - - -template -using meta_function_helper_t = decltype(to_meta_function_helper(std::declval())); - - -template -meta_any construct(meta_any * const args, std::index_sequence) { - [[maybe_unused]] auto direct = std::make_tuple((args+Indexes)->try_cast()...); - meta_any any{}; - - if(((std::get(direct) || (args+Indexes)->convert()) && ...)) { - any = Type{(std::get(direct) ? *std::get(direct) : (args+Indexes)->cast())...}; - } - - return any; -} - - -template -bool setter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index, [[maybe_unused]] meta_any value) { - bool accepted = false; - - if constexpr(!Const) { - if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { - using helper_type = meta_function_helper_t; - using data_type = std::tuple_element_t, typename helper_type::args_type>; - static_assert(std::is_invocable_v); - auto *direct = value.try_cast(); - auto *clazz = handle.data(); - - if(clazz && (direct || value.convert())) { - std::invoke(Data, *clazz, direct ? *direct : value.cast()); - accepted = true; - } - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_cv_t().*Data)>>; - static_assert(std::is_invocable_v); - auto *clazz = handle.data(); - - if constexpr(std::is_array_v) { - using underlying_type = std::remove_extent_t; - auto *direct = value.try_cast(); - auto *idx = index.try_cast(); - - if(clazz && idx && (direct || value.convert())) { - std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast(); - accepted = true; - } - } else { - auto *direct = value.try_cast(); - - if(clazz && (direct || value.convert())) { - std::invoke(Data, clazz) = (direct ? *direct : value.cast()); - accepted = true; - } - } - } else { - static_assert(std::is_pointer_v); - using data_type = std::remove_cv_t>; - - if constexpr(std::is_array_v) { - using underlying_type = std::remove_extent_t; - auto *direct = value.try_cast(); - auto *idx = index.try_cast(); - - if(idx && (direct || value.convert())) { - (*Data)[*idx] = (direct ? *direct : value.cast()); - accepted = true; - } - } else { - auto *direct = value.try_cast(); - - if(direct || value.convert()) { - *Data = (direct ? *direct : value.cast()); - accepted = true; - } - } - } - } - - return accepted; -} - - -template -meta_any getter([[maybe_unused]] meta_handle handle, [[maybe_unused]] meta_any index) { - auto dispatch = [](auto &&value) { - if constexpr(std::is_same_v) { - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{as_alias, std::forward(value)}; - } else { - static_assert(std::is_same_v); - return meta_any{std::forward(value)}; - } - }; - - if constexpr(std::is_function_v> || std::is_member_function_pointer_v) { - static_assert(std::is_invocable_v); - auto *clazz = handle.data(); - return clazz ? dispatch(std::invoke(Data, *clazz)) : meta_any{}; - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_cv_t().*Data)>>; - static_assert(std::is_invocable_v); - auto *clazz = handle.data(); - - if constexpr(std::is_array_v) { - auto *idx = index.try_cast(); - return (clazz && idx) ? dispatch(std::invoke(Data, clazz)[*idx]) : meta_any{}; - } else { - return clazz ? dispatch(std::invoke(Data, clazz)) : meta_any{}; - } - } else { - static_assert(std::is_pointer_v>); - - if constexpr(std::is_array_v>) { - auto *idx = index.try_cast(); - return idx ? dispatch((*Data)[*idx]) : meta_any{}; - } else { - return dispatch(*Data); - } - } -} - - -template -meta_any invoke([[maybe_unused]] meta_handle handle, meta_any *args, std::index_sequence) { - using helper_type = meta_function_helper_t; - - auto dispatch = [](auto *... args) { - if constexpr(std::is_void_v || std::is_same_v) { - std::invoke(Candidate, *args...); - return meta_any{std::in_place_type}; - } else if constexpr(std::is_same_v) { - return meta_any{as_alias, std::invoke(Candidate, *args...)}; - } else { - static_assert(std::is_same_v); - return meta_any{std::invoke(Candidate, *args...)}; - } - }; - - [[maybe_unused]] const auto direct = std::make_tuple([](meta_any *any, auto *instance) { - using arg_type = std::remove_reference_t; - - if(!instance && any->convert()) { - instance = any->try_cast(); - } - - return instance; - }(args+Indexes, (args+Indexes)->try_cast>())...); - - if constexpr(std::is_function_v>) { - return (std::get(direct) && ...) ? dispatch(std::get(direct)...) : meta_any{}; - } else { - auto *clazz = handle.data(); - return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : meta_any{}; - } -} - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/** - * @brief A meta factory to be used for reflection purposes. - * - * A meta factory is an utility class used to reflect types, data 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. - * - * @tparam Type Reflected type for which the factory was created. - */ -template -class meta_factory { - static_assert(std::is_same_v>); - - template - bool duplicate(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT { - return node ? node->identifier == identifier || duplicate(identifier, node->next) : false; - } - - bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { - return node ? node->key() == key || duplicate(key, node->next) : false; - } - - template - internal::meta_prop_node * properties() { - return nullptr; - } - - template - internal::meta_prop_node * properties(Property &&property, Other &&... other) { - static std::remove_cv_t> prop{}; - - static internal::meta_prop_node node{ - nullptr, - []() -> meta_any { - return std::as_const(std::get<0>(prop)); - }, - []() -> meta_any { - return std::as_const(std::get<1>(prop)); - }, - []() ENTT_NOEXCEPT -> meta_prop { - return &node; - } - }; - - prop = std::forward(property); - node.next = properties(std::forward(other)...); - ENTT_ASSERT(!duplicate(meta_any{std::get<0>(prop)}, node.next)); - return &node; - } - - void unregister_prop(internal::meta_prop_node **prop) { - while(*prop) { - auto *node = *prop; - *prop = node->next; - node->next = nullptr; - } - } - - void unregister_dtor() { - if(auto node = internal::meta_info::type->dtor; node) { - internal::meta_info::type->dtor = nullptr; - *node->underlying = nullptr; - } - } - - template - auto unregister_all(int) - -> decltype((internal::meta_info::type->*Member)->prop, void()) { - while(internal::meta_info::type->*Member) { - auto node = internal::meta_info::type->*Member; - internal::meta_info::type->*Member = node->next; - unregister_prop(&node->prop); - node->next = nullptr; - *node->underlying = nullptr; - } - } - - template - void unregister_all(char) { - while(internal::meta_info::type->*Member) { - auto node = internal::meta_info::type->*Member; - internal::meta_info::type->*Member = node->next; - node->next = nullptr; - *node->underlying = nullptr; - } - } - - meta_factory() ENTT_NOEXCEPT = default; - -public: - /** - * @brief Extends a meta type by assigning it an identifier and properties. - * @tparam Property Types of properties to assign to the meta type. - * @param identifier Unique identifier. - * @param property Properties to assign to the meta type. - * @return A meta factory for the parent type. - */ - template - meta_factory type(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - ENTT_ASSERT(!internal::meta_info::type); - auto *node = internal::meta_info::resolve(); - node->identifier = identifier; - node->next = internal::meta_info<>::type; - node->prop = properties(std::forward(property)...); - ENTT_ASSERT(!duplicate(identifier, node->next)); - internal::meta_info::type = node; - internal::meta_info<>::type = node; - - return *this; - } - - /** - * @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 - meta_factory base() ENTT_NOEXCEPT { - static_assert(std::is_base_of_v); - auto * const type = internal::meta_info::resolve(); - - static internal::meta_base_node node{ - &internal::meta_info::template base, - type, - nullptr, - &internal::meta_info::resolve, - [](void *instance) ENTT_NOEXCEPT -> void * { - return static_cast(static_cast(instance)); - }, - []() ENTT_NOEXCEPT -> meta_base { - return &node; - } - }; - - node.next = type->base; - ENTT_ASSERT((!internal::meta_info::template base)); - internal::meta_info::template base = &node; - type->base = &node; - - return *this; - } - - /** - * @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 - meta_factory conv() ENTT_NOEXCEPT { - static_assert(std::is_convertible_v); - auto * const type = internal::meta_info::resolve(); - - static internal::meta_conv_node node{ - &internal::meta_info::template conv, - type, - nullptr, - &internal::meta_info::resolve, - [](const void *instance) -> meta_any { - return static_cast(*static_cast(instance)); - }, - []() ENTT_NOEXCEPT -> meta_conv { - return &node; - } - }; - - node.next = type->conv; - ENTT_ASSERT((!internal::meta_info::template conv)); - internal::meta_info::template conv = &node; - type->conv = &node; - - return *this; - } - - /** - * @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 - 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{ - &internal::meta_info::template conv, - type, - nullptr, - &internal::meta_info::resolve, - [](const void *instance) -> meta_any { - return std::invoke(Candidate, *static_cast(instance)); - }, - []() ENTT_NOEXCEPT -> meta_conv { - return &node; - } - }; - - node.next = type->conv; - ENTT_ASSERT((!internal::meta_info::template conv)); - internal::meta_info::template conv = &node; - type->conv = &node; - - return *this; - } - - /** - * @brief Assigns a meta constructor to a meta type. - * - * Free functions 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 a free function. - * - * @tparam Func The actual function to use as a constructor. - * @tparam Policy Optional policy (no policy set by default). - * @tparam Property Types of properties to assign to the meta data. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. - */ - template - meta_factory ctor(Property &&... property) ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; - static_assert(std::is_same_v); - auto * const type = internal::meta_info::resolve(); - - static internal::meta_ctor_node node{ - &internal::meta_info::template ctor, - type, - nullptr, - nullptr, - helper_type::size, - &helper_type::arg, - [](meta_any * const any) { - return internal::invoke({}, any, std::make_index_sequence{}); - }, - []() ENTT_NOEXCEPT -> meta_ctor { - return &node; - } - }; - - node.next = type->ctor; - node.prop = properties(std::forward(property)...); - ENTT_ASSERT((!internal::meta_info::template ctor)); - internal::meta_info::template ctor = &node; - type->ctor = &node; - - return *this; - } - - /** - * @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. - * @tparam Property Types of properties to assign to the meta data. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. - */ - template - meta_factory ctor(Property &&... property) ENTT_NOEXCEPT { - using helper_type = internal::meta_function_helper_t; - auto * const type = internal::meta_info::resolve(); - - static internal::meta_ctor_node node{ - &internal::meta_info::template ctor, - type, - nullptr, - nullptr, - helper_type::size, - &helper_type::arg, - [](meta_any * const any) { - return internal::construct>...>(any, std::make_index_sequence{}); - }, - []() ENTT_NOEXCEPT -> meta_ctor { - return &node; - } - }; - - node.next = type->ctor; - node.prop = properties(std::forward(property)...); - ENTT_ASSERT((!internal::meta_info::template ctor)); - internal::meta_info::template ctor = &node; - type->ctor = &node; - - return *this; - } - - /** - * @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 - meta_factory dtor() ENTT_NOEXCEPT { - static_assert(std::is_invocable_v); - auto * const type = internal::meta_info::resolve(); - - static internal::meta_dtor_node node{ - &internal::meta_info::template dtor, - type, - [](meta_handle handle) { - return handle.type() == internal::meta_info::resolve()->meta() - ? (std::invoke(Func, *handle.data()), true) - : false; - }, - []() ENTT_NOEXCEPT -> meta_dtor { - return &node; - } - }; - - ENTT_ASSERT(!internal::meta_info::type->dtor); - ENTT_ASSERT((!internal::meta_info::template dtor)); - internal::meta_info::template dtor = &node; - internal::meta_info::type->dtor = &node; - - return *this; - } - - /** - * @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). - * @tparam Property Types of properties to assign to the meta data. - * @param identifier Unique identifier. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. - */ - template - meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - auto * const type = internal::meta_info::resolve(); - internal::meta_data_node *curr = nullptr; - - if constexpr(std::is_same_v) { - static_assert(std::is_same_v); - - static internal::meta_data_node node{ - &internal::meta_info::template data, - {}, - type, - nullptr, - nullptr, - true, - true, - &internal::meta_info::resolve, - [](meta_handle, meta_any, meta_any) { return false; }, - [](meta_handle, meta_any) -> meta_any { return Data; }, - []() ENTT_NOEXCEPT -> meta_data { - return &node; - } - }; - - node.prop = properties>(std::forward(property)...); - curr = &node; - } else if constexpr(std::is_member_object_pointer_v) { - using data_type = std::remove_reference_t().*Data)>; - - static internal::meta_data_node node{ - &internal::meta_info::template data, - {}, - type, - nullptr, - nullptr, - std::is_const_v, - !std::is_member_object_pointer_v, - &internal::meta_info::resolve, - &internal::setter, Type, Data>, - &internal::getter, - []() ENTT_NOEXCEPT -> meta_data { - return &node; - } - }; - - node.prop = properties>(std::forward(property)...); - curr = &node; - } else { - static_assert(std::is_pointer_v>); - using data_type = std::remove_pointer_t>; - - static internal::meta_data_node node{ - &internal::meta_info::template data, - {}, - type, - nullptr, - nullptr, - std::is_const_v, - !std::is_member_object_pointer_v, - &internal::meta_info::resolve, - &internal::setter, Type, Data>, - &internal::getter, - []() ENTT_NOEXCEPT -> meta_data { - return &node; - } - }; - - node.prop = properties>(std::forward(property)...); - curr = &node; - } - - curr->identifier = identifier; - curr->next = type->data; - ENTT_ASSERT(!duplicate(identifier, curr->next)); - ENTT_ASSERT((!internal::meta_info::template data)); - internal::meta_info::template data = curr; - type->data = curr; - - return *this; - } - - /** - * @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). - * @tparam Property Types of properties to assign to the meta data. - * @param identifier Unique identifier. - * @param property Properties to assign to the meta data. - * @return A meta factory for the parent type. - */ - template - meta_factory data(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - using owner_type = std::tuple, std::integral_constant>; - using underlying_type = std::invoke_result_t; - static_assert(std::is_invocable_v); - auto * const type = internal::meta_info::resolve(); - - static internal::meta_data_node node{ - &internal::meta_info::template data, - {}, - type, - nullptr, - nullptr, - false, - false, - &internal::meta_info::resolve, - &internal::setter, - &internal::getter, - []() ENTT_NOEXCEPT -> meta_data { - return &node; - } - }; - - node.identifier = identifier; - node.next = type->data; - node.prop = properties(std::forward(property)...); - ENTT_ASSERT(!duplicate(identifier, node.next)); - ENTT_ASSERT((!internal::meta_info::template data)); - internal::meta_info::template data = &node; - type->data = &node; - - return *this; - } - - /** - * @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). - * @tparam Property Types of properties to assign to the meta function. - * @param identifier Unique identifier. - * @param property Properties to assign to the meta function. - * @return A meta factory for the parent type. - */ - template - meta_factory func(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - using owner_type = std::integral_constant; - using helper_type = internal::meta_function_helper_t; - auto * const type = internal::meta_info::resolve(); - - static internal::meta_func_node node{ - &internal::meta_info::template func, - {}, - type, - nullptr, - nullptr, - helper_type::size, - helper_type::is_const, - !std::is_member_function_pointer_v, - &internal::meta_info, void, typename helper_type::return_type>>::resolve, - &helper_type::arg, - [](meta_handle handle, meta_any *any) { - return internal::invoke(handle, any, std::make_index_sequence{}); - }, - []() ENTT_NOEXCEPT -> meta_func { - return &node; - } - }; - - node.identifier = identifier; - node.next = type->func; - node.prop = properties(std::forward(property)...); - ENTT_ASSERT(!duplicate(identifier, node.next)); - ENTT_ASSERT((!internal::meta_info::template func)); - internal::meta_info::template func = &node; - type->func = &node; - - return *this; - } - - /** - * @brief Unregisters a meta type and all its parts. - * - * This function unregisters a meta 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 unregistered but the link between the two types is - * removed. - * - * @return True if the meta type exists, false otherwise. - */ - bool unregister() ENTT_NOEXCEPT { - const auto registered = internal::meta_info::type; - - if(registered) { - if(auto *curr = internal::meta_info<>::type; curr == internal::meta_info::type) { - internal::meta_info<>::type = internal::meta_info::type->next; - } else { - while(curr && curr->next != internal::meta_info::type) { - curr = curr->next; - } - - if(curr) { - curr->next = internal::meta_info::type->next; - } - } - - unregister_prop(&internal::meta_info::type->prop); - unregister_all<&internal::meta_type_node::base>(0); - unregister_all<&internal::meta_type_node::conv>(0); - unregister_all<&internal::meta_type_node::ctor>(0); - unregister_all<&internal::meta_type_node::data>(0); - unregister_all<&internal::meta_type_node::func>(0); - unregister_dtor(); - - internal::meta_info::type->identifier = {}; - internal::meta_info::type->next = nullptr; - internal::meta_info::type = nullptr; - } - - return registered; - } -}; - - -/** - * @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 data and functions through - * a dedicated factory. - * - * @tparam Type Type to reflect. - * @tparam Property Types of properties to assign to the reflected type. - * @param identifier Unique identifier. - * @param property Properties to assign to the reflected type. - * @return A meta factory for the given type. - */ -template -inline meta_factory reflect(const ENTT_ID_TYPE identifier, Property &&... property) ENTT_NOEXCEPT { - return meta_factory{}.type(identifier, std::forward(property)...); -} - - -/** - * @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 data and functions through - * a dedicated factory. - * - * @tparam Type Type to reflect. - * @return A meta factory for the given type. - */ -template -inline meta_factory reflect() ENTT_NOEXCEPT { - return meta_factory{}; -} - - -/** - * @brief Utility function to unregister a type. - * - * This function unregisters 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 unregistered but the link between the two types is - * removed. - * - * @tparam Type Type to unregister. - * @return True if the type to unregister exists, false otherwise. - */ -template -inline bool unregister() ENTT_NOEXCEPT { - return meta_factory{}.unregister(); -} - - -/** - * @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 -inline meta_type resolve() ENTT_NOEXCEPT { - return internal::meta_info::resolve()->meta(); -} - - -/** - * @brief Returns the meta type associated with a given identifier. - * @param identifier Unique identifier. - * @return The meta type associated with the given identifier, if any. - */ -inline meta_type resolve(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { - const auto *curr = internal::find_if([identifier](auto *node) { - return node->identifier == identifier; - }, internal::meta_info<>::type); - - return curr ? curr->meta() : meta_type{}; -} - - -/** - * @brief Iterates all the reflected types. - * @tparam Op Type of the function object to invoke. - * @param op A valid function object. - */ -template -inline std::enable_if_t, void> -resolve(Op op) ENTT_NOEXCEPT { - internal::iterate([op = std::move(op)](auto *node) { - op(node->meta()); - }, internal::meta_info<>::type); -} - - -} - - -#endif // ENTT_META_FACTORY_HPP - -// #include "meta/meta.hpp" - -// #include "meta/policy.hpp" - -// #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 - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_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 - }; - - template - using state_value_t = std::integral_constant; - - template - auto tick(int, state_value_t) - -> decltype(std::declval().init()) { - static_cast(this)->init(); - } - - template - auto tick(int, state_value_t, Delta delta, void *data) - -> decltype(std::declval().update(delta, data)) { - static_cast(this)->update(delta, data); - } - - template - auto tick(int, state_value_t) - -> decltype(std::declval().succeeded()) { - static_cast(this)->succeeded(); - } - - template - auto tick(int, state_value_t) - -> decltype(std::declval().failed()) { - static_cast(this)->failed(); - } - - template - auto tick(int, state_value_t) - -> decltype(std::declval().aborted()) { - static_cast(this)->aborted(); - } - - template - void tick(char, state_value_t, Args &&...) 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() ENTT_NOEXCEPT { - static_assert(std::is_base_of_v); - } - - /** - * @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) ENTT_NOEXCEPT { - if(alive()) { - current = state::ABORTED; - - if(immediately) { - tick(0); - } - } - } - - /** - * @brief Returns true if a process is either running or paused. - * @return True if the process is still alive, false otherwise. - */ - 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. - */ - bool dead() 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. - */ - 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. - */ - bool rejected() const ENTT_NOEXCEPT { - return stopped; - } - - /** - * @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: - tick(0, state_value_t{}); - current = state::RUNNING; - break; - case state::RUNNING: - tick(0, state_value_t{}, delta, data); - break; - default: - // suppress warnings - break; - } - - // if it's dead, it must be notified and removed immediately - switch(current) { - case state::SUCCEEDED: - tick(0, state_value_t{}); - current = state::FINISHED; - break; - case state::FAILED: - tick(0, state_value_t{}); - current = state::FINISHED; - stopped = true; - break; - case state::ABORTED: - tick(0, state_value_t{}); - current = state::FINISHED; - stopped = true; - break; - default: - // suppress warnings - break; - } - } - -private: - state current{state::UNINITIALIZED}; - bool stopped{false}; -}; - - -/** - * @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 // ENTT_PROCESS_PROCESS_HPP - -// #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" - - - -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} - { - ENTT_ASSERT(handler); - } - - template - continuation then(Args &&... args) { - static_assert(std::is_base_of_v, Proc>); - 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 - static bool update(process_handler &handler, const Delta delta, void *data) { - auto *process = static_cast(handler.instance.get()); - process->tick(delta, data); - - auto dead = process->dead(); - - if(dead) { - if(handler.next && !process->rejected()) { - handler = std::move(*handler.next); - // forces the process to exit the uninitialized state - dead = handler.update(handler, {}, nullptr); - } else { - handler.instance.reset(); - } - } - - return dead; - } - - 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 = typename std::vector::size_type; - - /*! @brief Default constructor. */ - scheduler() ENTT_NOEXCEPT = 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. - */ - 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. - */ - 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>); - 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) { - bool clean = false; - - for(auto pos = handlers.size(); pos; --pos) { - auto &handler = handlers[pos-1]; - const bool dead = handler.update(handler, delta, data); - clean = clean || dead; - } - - if(clean) { - handlers.erase(std::remove_if(handlers.begin(), handlers.end(), [](auto &handler) { - return !handler.instance; - }), 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); - - std::for_each(exec.begin(), exec.end(), [immediately](auto &handler) { - handler.abort(handler, immediately); - }); - - std::move(handlers.begin(), handlers.end(), std::back_inserter(exec)); - handlers.swap(exec); - } - -private: - std::vector handlers{}; -}; - - -} - - -#endif // ENTT_PROCESS_SCHEDULER_HPP - -// #include "resource/cache.hpp" -#ifndef ENTT_RESOURCE_CACHE_HPP -#define ENTT_RESOURCE_CACHE_HPP - - -#include -#include -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - -// #include "handle.hpp" -#ifndef ENTT_RESOURCE_HANDLE_HPP -#define ENTT_RESOURCE_HANDLE_HPP - - -#include -#include -// #include "../config/config.h" - -// #include "fwd.hpp" -#ifndef ENTT_RESOURCE_FWD_HPP -#define ENTT_RESOURCE_FWD_HPP - - -// #include "../config/config.h" - - - -namespace entt { - - -/*! @class resource_cache */ -template -class resource_cache; - -/*! @class resource_handle */ -template -class resource_handle; - -/*! @class resource_loader */ -template -class resource_loader; - - -} - - -#endif // ENTT_RESOURCE_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 of their caches. */ - friend class resource_cache; - - resource_handle(std::shared_ptr res) ENTT_NOEXCEPT - : resource{std::move(res)} - {} - -public: - /*! @brief Default constructor. */ - resource_handle() ENTT_NOEXCEPT = default; - - /** - * @brief Gets a reference to the managed resource. - * - * @warning - * The behavior is undefined if the handle doesn't contain a resource.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. - * - * @return A reference to the managed resource. - */ - const Resource & get() const ENTT_NOEXCEPT { - ENTT_ASSERT(static_cast(resource)); - return *resource; - } - - /*! @copydoc get */ - Resource & get() ENTT_NOEXCEPT { - return const_cast(std::as_const(*this).get()); - } - - /*! @copydoc get */ - operator const Resource & () const ENTT_NOEXCEPT { return get(); } - - /*! @copydoc get */ - operator Resource & () ENTT_NOEXCEPT { return get(); } - - /*! @copydoc get */ - const Resource & operator *() const ENTT_NOEXCEPT { return get(); } - - /*! @copydoc get */ - 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.
- * An assertion will abort the execution at runtime in debug mode if the - * handle is empty. - * - * @return A pointer to the managed resource or `nullptr` if the handle - * contains no resource at all. - */ - const Resource * operator->() const ENTT_NOEXCEPT { - ENTT_ASSERT(static_cast(resource)); - return resource.get(); - } - - /*! @copydoc operator-> */ - 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. - */ - explicit operator bool() const { return static_cast(resource); } - -private: - std::shared_ptr resource; -}; - - -} - - -#endif // ENTT_RESOURCE_HANDLE_HPP - -// #include "loader.hpp" -#ifndef ENTT_RESOURCE_LOADER_HPP -#define ENTT_RESOURCE_LOADER_HPP - - -#include -// #include "fwd.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 shared pointer to the resource just created.
- * As an example: - * - * @code{.cpp} - * struct my_resource {}; - * - * struct my_loader: entt::resource_loader { - * std::shared_ptr load(int) 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. */ - friend class 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 - std::shared_ptr get(Args &&... args) const { - return static_cast(this)->load(std::forward(args)...); - } -}; - - -} - - -#endif // ENTT_RESOURCE_LOADER_HPP - -// #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 -class resource_cache { - using container_type = std::unordered_map>; - -public: - /*! @brief Unsigned integer type. */ - using size_type = typename container_type::size_type; - /*! @brief Type of resources managed by a cache. */ - using resource_type = ENTT_ID_TYPE; - - /*! @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. - */ - 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. - */ - 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 resource_type id, Args &&... args) { - static_assert(std::is_base_of_v, Loader>); - resource_handle handle{}; - - if(auto it = resources.find(id); it == resources.cend()) { - if(auto resource = Loader{}.get(std::forward(args)...); resource) { - resources[id] = resource; - handle = std::move(resource); - } - } else { - handle = it->second; - } - - return handle; - } - - /** - * @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 resource_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 - 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. - */ - resource_handle handle(const resource_type id) const { - auto it = resources.find(id); - return { it == resources.end() ? nullptr : it->second }; - } - - /** - * @brief Checks if a cache contains a given identifier. - * @param id Unique resource identifier. - * @return True if the cache contains the resource, false otherwise. - */ - bool contains(const resource_type id) const ENTT_NOEXCEPT { - 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 resource_type id) ENTT_NOEXCEPT { - 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 resource_type); - * void(resource_handle); - * void(const resource_type, 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(resource_handle{ curr->second }); - } else { - func(curr->first, resource_handle{ curr->second }); - } - } - } - -private: - container_type resources; -}; - - -} - - -#endif // ENTT_RESOURCE_CACHE_HPP - -// #include "resource/handle.hpp" - -// #include "resource/loader.hpp" - -// #include "signal/delegate.hpp" -#ifndef ENTT_SIGNAL_DELEGATE_HPP -#define ENTT_SIGNAL_DELEGATE_HPP - - +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 -#include -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - - - -namespace entt { - - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -namespace internal { - - -template -auto to_function_pointer(Ret(*)(Args...)) -> Ret(*)(Args...); - - -template>> -auto to_function_pointer(Ret(*)(Type &, Args...), Payload &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...); - - -template -auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)(); - - -template -struct function_extent; - - -template -struct function_extent { - static constexpr auto value = sizeof...(Args); -}; - - -template -constexpr auto function_extent_v = function_extent::value; - - -} - - -/** - * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN - */ - - -/*! @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 -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 general purpose invoker with no memory overhead for - * free functions (with or without payload) and members provided along with an - * instance on which to invoke them. - * - * @tparam Ret Return type of a function type. - * @tparam Args Types of arguments of a function type. - */ -template -class delegate { - using proto_fn_type = Ret(const void *, std::tuple); - - template - void connect(std::index_sequence) ENTT_NOEXCEPT { - static_assert(std::is_invocable_r_v>...>); - data = nullptr; - - fn = [](const void *, std::tuple args) -> Ret { - // Ret(...) makes void(...) eat the return values to avoid errors - return Ret(std::invoke(Function, std::forward>>(std::get(args))...)); - }; - } - - template - void connect(Type &value_or_instance, std::index_sequence) ENTT_NOEXCEPT { - static_assert(std::is_invocable_r_v>...>); - data = &value_or_instance; - - fn = [](const void *payload, std::tuple args) -> Ret { - Type *curr = nullptr; - - if constexpr(std::is_const_v) { - curr = static_cast(payload); - } else { - curr = static_cast(const_cast(payload)); - } - - // Ret(...) makes void(...) eat the return values to avoid errors - return Ret(std::invoke(Candidate, *curr, std::forward>>(std::get(args))...)); - }; - } - -public: - /*! @brief Function type of the delegate. */ - using function_type = Ret(Args...); - - /*! @brief Default constructor. */ - delegate() ENTT_NOEXCEPT - : fn{nullptr}, data{nullptr} - {} - - /** - * @brief Constructs a delegate and connects a free function to it. - * @tparam Function A valid free function pointer. - */ - template - delegate(connect_arg_t) ENTT_NOEXCEPT - : delegate{} - { - connect(); - } - - /** - * @brief Constructs a delegate and connects a member for a given instance - * or a free function with payload. - * @tparam Candidate Member or free function 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 - delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT - : delegate{} - { - connect(value_or_instance); - } - - /** - * @brief Connects a free function to a delegate. - * @tparam Function A valid free function pointer. - */ - template - void connect() ENTT_NOEXCEPT { - constexpr auto extent = internal::function_extent_v()))>; - connect(std::make_index_sequence{}); - } - - /** - * @brief Connects a member function for a given instance or a free function - * with payload 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 Member or free function 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 { - constexpr auto extent = internal::function_extent_v(), value_or_instance))>; - connect(value_or_instance, std::make_index_sequence{}); - } - - /** - * @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. - */ - 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.
- * An assertion will abort the execution at runtime in debug mode if the - * delegate has not yet been set. - * - * @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(fn); - return fn(data, std::forward_as_tuple(std::forward(args)...)); - } - - /** - * @brief Checks whether a delegate actually stores a listener. - * @return False if the delegate is empty, true otherwise. - */ - explicit operator bool() const ENTT_NOEXCEPT { - // no need to test also data - return fn; - } - - /** - * @brief Compares the contents of two delegates. - * @param other Delegate with which to compare. - * @return False if the two contents differ, true otherwise. - */ - bool operator==(const delegate &other) const ENTT_NOEXCEPT { - return fn == other.fn && data == other.data; - } - -private: - proto_fn_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 -bool operator!=(const delegate &lhs, const delegate &rhs) ENTT_NOEXCEPT { - return !(lhs == rhs); -} - - -/** - * @brief Deduction guide. - * - * It allows to deduce the function type of the delegate directly from a - * function provided to the constructor. - * - * @tparam Function A valid free function pointer. - */ -template -delegate(connect_arg_t) ENTT_NOEXCEPT --> delegate>; - - -/** - * @brief Deduction guide. - * - * It allows to deduce the function type of the delegate directly from a member - * or a free function with payload provided to the constructor. - * - * @param value_or_instance A valid reference that fits the purpose. - * @tparam Candidate Member or free function to connect to the delegate. - * @tparam Type Type of class or type of payload. - */ -template -delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT --> delegate>; - - -} - - -#endif // ENTT_SIGNAL_DELEGATE_HPP - -// #include "signal/dispatcher.hpp" -#ifndef ENTT_SIGNAL_DISPATCHER_HPP -#define ENTT_SIGNAL_DISPATCHER_HPP - - -#include -#include -#include -#include -// #include "../config/config.h" - -// #include "../core/family.hpp" -#ifndef ENTT_CORE_FAMILY_HPP -#define ENTT_CORE_FAMILY_HPP - - -#include -// #include "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE -#include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H - - - -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(ENTT_ID_TYPE) identifier; - - template - // clang (since version 9) started to complain if auto is used instead of ENTT_ID_TYPE - inline static const ENTT_ID_TYPE inner = identifier++; - -public: - /*! @brief Unsigned integer type. */ - using family_type = ENTT_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 = inner...>; -}; - - -} - - -#endif // ENTT_CORE_FAMILY_HPP - -// #include "../core/type_traits.hpp" -#ifndef ENTT_CORE_TYPE_TRAITS_HPP -#define ENTT_CORE_TYPE_TRAITS_HPP - - -#include -// #include "../config/config.h" - -// #include "../core/hashed_string.hpp" +#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 "../config/config.h" -#ifndef ENTT_CONFIG_CONFIG_H -#define ENTT_CONFIG_CONFIG_H - - -#ifndef ENTT_NOEXCEPT -#define ENTT_NOEXCEPT noexcept -#endif // ENTT_NOEXCEPT - - -#ifndef ENTT_HS_SUFFIX -#define ENTT_HS_SUFFIX _hs -#endif // ENTT_HS_SUFFIX - - -#ifndef ENTT_HWS_SUFFIX -#define ENTT_HWS_SUFFIX _hws -#endif // ENTT_HWS_SUFFIX - - -#ifndef ENTT_NO_ATOMIC -#include -#define ENTT_MAYBE_ATOMIC(Type) std::atomic -#else // ENTT_NO_ATOMIC -#define ENTT_MAYBE_ATOMIC(Type) Type -#endif // ENTT_NO_ATOMIC - - -#ifndef ENTT_ID_TYPE #include -#define ENTT_ID_TYPE std::uint32_t -#endif // ENTT_ID_TYPE - - -#ifndef ENTT_PAGE_SIZE -#define ENTT_PAGE_SIZE 32768 -#endif // ENTT_PAGE_SIZE - - -#ifndef ENTT_DISABLE_ASSERT -#include -#define ENTT_ASSERT(condition) assert(condition) -#else // ENTT_DISABLE_ASSERT -#define ENTT_ASSERT(...) ((void)0) -#endif // ENTT_DISABLE_ASSERT - - -#endif // ENTT_CONFIG_CONFIG_H +// #include "../config/config.h" + +// #include "fwd.hpp" @@ -14823,6 +30000,7 @@ template<> struct fnv1a_traits { + using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; @@ -14830,6 +30008,7 @@ template<> struct fnv1a_traits { + using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; @@ -14840,7 +30019,7 @@ /** * Internal details not to be documented. - * @endcond TURN_OFF_DOXYGEN + * @endcond */ @@ -14857,7 +30036,7 @@ */ template class basic_hashed_string { - using traits_type = internal::fnv1a_traits; + using traits_type = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose @@ -14866,15 +30045,33 @@ }; // Fowler–Noll–Vo hash function v. 1a - the good - static constexpr ENTT_ID_TYPE helper(ENTT_ID_TYPE partial, const Char *curr) ENTT_NOEXCEPT { - return curr[0] == 0 ? partial : helper((partial^curr[0])*traits_type::prime, curr+1); + [[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 = ENTT_ID_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. @@ -14892,8 +30089,8 @@ * @return The numeric representation of the string. */ template - static constexpr hash_type to_value(const value_type (&str)[N]) ENTT_NOEXCEPT { - return helper(traits_type::offset, str); + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) ENTT_NOEXCEPT { + return helper(str); } /** @@ -14901,20 +30098,8 @@ * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ - static hash_type to_value(const_wrapper wrapper) ENTT_NOEXCEPT { - return helper(traits_type::offset, wrapper.str); - } - - /** - * @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. - */ - static hash_type to_value(const value_type *str, std::size_t size) ENTT_NOEXCEPT { - ENTT_ID_TYPE partial{traits_type::offset}; - while(size--) { partial = (partial^(str++)[0])*traits_type::prime; } - return partial; + [[nodiscard]] static hash_type value(const_wrapper wrapper) ENTT_NOEXCEPT { + return helper(wrapper.str); } /*! @brief Constructs an empty hashed string. */ @@ -14938,7 +30123,7 @@ */ template constexpr basic_hashed_string(const value_type (&curr)[N]) ENTT_NOEXCEPT - : str{curr}, hash{helper(traits_type::offset, curr)} + : str{curr}, hash{helper(curr)} {} /** @@ -14947,14 +30132,14 @@ * @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(traits_type::offset, wrapper.str)} + : 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. */ - constexpr const value_type * data() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr const value_type * data() const ENTT_NOEXCEPT { return str; } @@ -14962,25 +30147,25 @@ * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the instance. */ - constexpr hash_type value() const ENTT_NOEXCEPT { + [[nodiscard]] constexpr hash_type value() const ENTT_NOEXCEPT { return hash; } - /** - * @brief Returns the human-readable representation of a hashed string. - * @return The string used to initialize the instance. - */ - constexpr operator const value_type *() const ENTT_NOEXCEPT { return str; } - - /*! @copydoc value */ - constexpr operator hash_type() 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. */ - constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { + [[nodiscard]] constexpr bool operator==(const basic_hashed_string &other) const ENTT_NOEXCEPT { return hash == other.hash; } @@ -15001,7 +30186,7 @@ * @param str Human-readable identifer. */ template -basic_hashed_string(const Char (&str)[N]) ENTT_NOEXCEPT +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; @@ -15013,7 +30198,7 @@ * @return True if the two hashed strings are identical, false otherwise. */ template -constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) ENTT_NOEXCEPT { return !(lhs == rhs); } @@ -15026,7 +30211,7 @@ using hashed_wstring = basic_hashed_string; -} +inline namespace literals { /** @@ -15034,7 +30219,7 @@ * @param str The literal without its suffix. * @return A properly initialized hashed string. */ -constexpr entt::hashed_string operator"" ENTT_HS_SUFFIX(const char *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_string operator"" _hs(const char *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_string{str}; } @@ -15044,44 +30229,945 @@ * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ -constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { +[[nodiscard]] constexpr entt::hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) ENTT_NOEXCEPT { return entt::hashed_wstring{str}; } -#endif // ENTT_CORE_HASHED_STRING_HPP - - - -namespace entt { - - -/*! @brief A class to use to push around lists of types, nothing more. */ -template -struct type_list {}; - - -/*! @brief Primary template isn't defined on purpose. */ -template -struct type_list_size; - - -/** - * @brief Compile-time number of elements in a type list. +} + + +} + + +#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_size> - : std::integral_constant -{}; - - -/** - * @brief Helper variable template. - * @tparam List Type list. - */ -template -constexpr auto type_list_size_v = type_list_size::value; +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. */ @@ -15166,197 +31252,15764 @@ using type_list_unique_t = typename type_list_unique::type; -/*! @brief Traits class used mainly to push things across boundaries. */ -template -struct named_type_traits; - - -/** - * @brief Specialization used to get rid of constness. - * @tparam Type Named type. - */ -template -struct named_type_traits - : named_type_traits -{}; - - -/** - * @brief Helper type. - * @tparam Type Potentially named type. - */ -template -using named_type_traits_t = typename named_type_traits::type; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - */ -template> -struct is_named_type: std::false_type {}; - - -/** - * @brief Provides the member constant `value` to true if a given type has a - * name. In all other cases, `value` is false. - * @tparam Type Potentially named type. - */ -template -struct is_named_type>>>: std::true_type {}; - - -/** - * @brief Helper variable template. - * @tparam Type Potentially named 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 -constexpr auto is_named_type_v = is_named_type::value; - - -/** - * @brief Defines an enum class to use for opaque identifiers and a dedicate - * `to_integer` function to convert the identifiers to their underlying type. - * @param clazz The name to use for the enum class. - * @param type The underlying type for the enum class. - */ -#define ENTT_OPAQUE_TYPE(clazz, type)\ - enum class clazz: type {};\ - constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ - return std::underlying_type_t(id);\ - } - - -} - - -/** - * @brief Utility macro to deal with an issue of MSVC. - * - * See _msvc-doesnt-expand-va-args-correctly_ on SO for all the details. - * - * @param args Argument to expand. - */ -#define ENTT_EXPAND(args) args - - -/** - * @brief Makes an already existing type a named type. - * - * The current definition contains a workaround for Clang 6 because it fails to - * deduce correctly the type to use to specialize the class template.
- * With a compiler that fully supports C++17 and works fine with deduction - * guides, the following should be fine instead: +inline constexpr bool is_equality_comparable_v = is_equality_comparable::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))); + } + } + break; + 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))); + } + } + break; + 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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} - * std::integral_constant + * 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 order to support even sligthly older compilers, I prefer to stick to the - * implementation below. - * - * @param type Type to assign a name to. - */ -#define ENTT_NAMED_TYPE(type)\ - template<>\ - struct entt::named_type_traits\ - : std::integral_constant>>>{#type}>\ - {\ - static_assert(std::is_same_v, type>);\ - }; - - -/** - * @brief Defines a named type (to use for structs). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_ONLY(clazz, body)\ - struct clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for structs). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_STRUCT_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { struct clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_STRUCT_OVERLOAD(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for structs). */ -#define ENTT_NAMED_STRUCT(...) ENTT_EXPAND(ENTT_NAMED_STRUCT_OVERLOAD(__VA_ARGS__, ENTT_NAMED_STRUCT_WITH_NAMESPACE, ENTT_NAMED_STRUCT_ONLY,)(__VA_ARGS__)) - - -/** - * @brief Defines a named type (to use for classes). - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_ONLY(clazz, body)\ - class clazz body;\ - ENTT_NAMED_TYPE(clazz) - - -/** - * @brief Defines a named type (to use for classes). - * @param ns Namespace where to define the named type. - * @param clazz Name of the type to define. - * @param body Body of the type to define. - */ -#define ENTT_NAMED_CLASS_WITH_NAMESPACE(ns, clazz, body)\ - namespace ns { class clazz body; }\ - ENTT_NAMED_TYPE(ns::clazz) - - -/*! @brief Utility function to simulate macro overloading. */ -#define ENTT_NAMED_CLASS_MACRO(_1, _2, _3, FUNC, ...) FUNC -/*! @brief Defines a named type (to use for classes). */ -#define ENTT_NAMED_CLASS(...) ENTT_EXPAND(ENTT_NAMED_CLASS_MACRO(__VA_ARGS__, ENTT_NAMED_CLASS_WITH_NAMESPACE, ENTT_NAMED_CLASS_ONLY,)(__VA_ARGS__)) - - -#endif // ENTT_CORE_TYPE_TRAITS_HPP + * 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; + + +/*! @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; + + +/** + * @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(is_iterator_v) { + return true; + } else 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 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 #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 -// #include "../config/config.h" - - - -namespace entt { - - -/*! @class delegate */ +namespace entt { + + template class delegate; -/*! @class sink */ + +class dispatcher; + + +template +class emitter; + + +class connection; + + +struct scoped_connection; + + template class sink; -/*! @class sigh */ + template class sigh; @@ -15364,7 +47017,7 @@ } -#endif // ENTT_SIGNAL_FWD_HPP +#endif @@ -15417,9 +47070,9 @@ public: /*! @brief Unsigned integer type. */ - using size_type = typename std::vector>::size_type; + using size_type = std::size_t; /*! @brief Sink type. */ - using sink_type = entt::sink; + using sink_type = sink; /** * @brief Instance type when it comes to connecting member functions. @@ -15432,7 +47085,7 @@ * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ - size_type size() const ENTT_NOEXCEPT { + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { return calls.size(); } @@ -15440,7 +47093,7 @@ * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ - bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return calls.empty(); } @@ -15452,8 +47105,8 @@ * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { - for(auto pos = calls.size(); pos; --pos) { - calls[pos-1](args...); + for(auto &&call: std::as_const(calls)) { + call(args...); } } @@ -15473,22 +47126,20 @@ */ template void collect(Func func, Args... args) const { - bool stop = false; - - for(auto pos = calls.size(); pos && !stop; --pos) { + for(auto &&call: calls) { if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { - calls[pos-1](args...); - stop = func(); + call(args...); + if(func()) { break; } } else { - calls[pos-1](args...); + call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { - stop = func(calls[pos-1](args...)); + if(func(call(args...))) { break; } } else { - func(calls[pos-1](args...)); + func(call(args...)); } } } @@ -15516,46 +47167,14 @@ {} public: - /*! Default constructor. */ + /*! @brief Default constructor. */ connection() = default; - /*! @brief Default copy constructor. */ - connection(const connection &) = default; - - /** - * @brief Default move constructor. - * @param other The instance to move from. - */ - connection(connection &&other) - : connection{} - { - std::swap(disconnect, other.disconnect); - std::swap(signal, other.signal); - } - - /*! @brief Default copy assignment operator. @return This connection. */ - connection & operator=(const connection &) = default; - - /** - * @brief Default move assignment operator. - * @param other The instance to move from. - * @return This connection. - */ - connection & operator=(connection &&other) { - if(this != &other) { - auto tmp{std::move(other)}; - disconnect = tmp.disconnect; - signal = tmp.signal; - } - - return *this; - } - /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ - explicit operator bool() const ENTT_NOEXCEPT { + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { return static_cast(disconnect); } @@ -15582,27 +47201,24 @@ * A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ -struct scoped_connection: private connection { - /*! Default constructor. */ +struct scoped_connection { + /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. - * @param conn A valid connection object. - */ - scoped_connection(const connection &conn) - : connection{conn} + * @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 Default move constructor. */ - scoped_connection(scoped_connection &&) = default; - /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { - connection::release(); + conn.release(); } /** @@ -15612,33 +47228,30 @@ scoped_connection & operator=(const scoped_connection &) = delete; /** - * @brief Default move assignment operator. - * @return This scoped connection. - */ - scoped_connection & operator=(scoped_connection &&) = default; - - /** - * @brief Copies a connection. - * @param other The connection object to copy. + * @brief Acquires a connection. + * @param other The connection object to acquire. * @return This scoped connection. */ - scoped_connection & operator=(const connection &other) { - static_cast(*this) = other; - return *this; - } - - /** - * @brief Moves a connection. - * @param other The connection object to move. - * @return This scoped connection. - */ - scoped_connection & operator=(connection &&other) { - static_cast(*this) = std::move(other); - return *this; - } - - using connection::operator bool; - using connection::release; + 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; }; @@ -15653,21 +47266,26 @@ * 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) { + static void release(Type value_or_instance, void *signal) { sink{*static_cast(signal)}.disconnect(value_or_instance); } - template + template static void release(void *signal) { - sink{*static_cast(signal)}.disconnect(); + sink{*static_cast(signal)}.disconnect(); } public: @@ -15676,100 +47294,207 @@ * @param ref A valid reference to a signal object. */ sink(sigh &ref) ENTT_NOEXCEPT - : signal{&ref} + : 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. */ - bool empty() const ENTT_NOEXCEPT { + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { return signal->calls.empty(); } /** - * @brief Connects a free function to a signal. - * - * The signal handler performs checks to avoid multiple connections for free - * functions. - * + * @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 connection object. + * @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(); + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + delegate conn{}; - conn.template connect<&release>(); - signal->calls.emplace_back(delegate{connect_arg}); + conn.template connect<&release>(); return { std::move(conn), signal }; } /** - * @brief Connects a member function or a free function with payload to a + * @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 delegate. On the other side, the signal handler performs + * 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 delegate itself. - * - * @tparam Candidate Member or free function to connect to the signal. + * 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 reference that fits the purpose. + * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized connection object. */ template - connection connect(Type &value_or_instance) { + 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<&sink::release>(value_or_instance); - signal->calls.emplace_back(delegate{connect_arg, value_or_instance}); + conn.template connect<&release>(value_or_instance); return { std::move(conn), signal }; } /** - * @brief Disconnects a free function from a signal. - * @tparam Function A valid free function pointer. - */ - template + * @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 delegate{}; - delegate.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); - } - - /** - * @brief Disconnects a member function or a free function with payload from - * a signal. - * @tparam Candidate Member or free function to disconnect from the signal. + 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 reference that fits the purpose. + * @param value_or_instance A valid object that fits the purpose. */ template - void disconnect(Type &value_or_instance) { + void disconnect(Type &&value_or_instance) { auto &calls = signal->calls; - delegate delegate{}; - delegate.template connect(value_or_instance); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); - } - - /** - * @brief Disconnects member functions or free functions based on an - * instance or specific payload. + 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 reference that fits the purpose. - */ - template - void disconnect(const Type &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()); + * @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. */ @@ -15778,6 +47503,7 @@ } private: + difference_type offset; signal_type *signal; }; @@ -15792,13 +47518,14 @@ * @tparam Args Types of arguments of a function type. */ template -sink(sigh &) ENTT_NOEXCEPT -> sink; - - -} - - -#endif // ENTT_SIGNAL_SIGH_HPP +sink(sigh &) +-> sink; + + +} + + +#endif @@ -15812,117 +47539,101 @@ * 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 `const Event &`, no matter what the return type is. - * - * The types of the instances are `Class &`. Users must guarantee that the - * lifetimes of the objects overcome the one of the dispatcher itself to avoid - * crashes. + * 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 { - using event_family = family; - - template - using instance_type = typename sigh::template instance_type; - - struct base_wrapper { - virtual ~base_wrapper() = default; + struct basic_pool { + virtual ~basic_pool() = default; virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() ENTT_NOEXCEPT = 0; }; template - struct signal_wrapper: base_wrapper { - using signal_type = sigh; + 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 { - for(const auto &event: events[current]) { - signal.publish(event); - } - - events[current++].clear(); - current %= std::extent::value; - } - - sink_type sink() ENTT_NOEXCEPT { + 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) { - signal.publish({ std::forward(args)... }); + Event instance{std::forward(args)...}; + signal.publish(instance); } template void enqueue(Args &&... args) { - events[current].emplace_back(std::forward(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[2]; - int current{}; - }; - - struct wrapper_data { - std::unique_ptr wrapper; - ENTT_ID_TYPE runtime_type; + std::vector events; }; template - static auto type() ENTT_NOEXCEPT { - if constexpr(is_named_type_v) { - return named_type_traits::value; - } else { - return event_family::type; - } - } - - template - signal_wrapper & assure() { - const auto wtype = type(); - wrapper_data *wdata = nullptr; - - if constexpr(is_named_type_v) { - const auto it = std::find_if(wrappers.begin(), wrappers.end(), [wtype](const auto &candidate) { - return candidate.wrapper && candidate.runtime_type == wtype; - }); - - wdata = (it == wrappers.cend() ? &wrappers.emplace_back() : &(*it)); - } else { - if(!(wtype < wrappers.size())) { - wrappers.resize(wtype+1); - } - - wdata = &wrappers[wtype]; - - if(wdata->wrapper && wdata->runtime_type != wtype) { - wrappers.emplace_back(); - std::swap(wrappers[wtype], wrappers.back()); - wdata = &wrappers[wtype]; - } - } - - if(!wdata->wrapper) { - wdata->wrapper = std::make_unique>(); - wdata->runtime_type = wtype; - } - - return static_cast &>(*wdata->wrapper); - } - -public: - /*! @brief Type of sink for the given event. */ - template - using sink_type = typename signal_wrapper::sink_type; + [[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: - * @code{.cpp} - * void(const Event &); + * The function type for a listener is _compatible_ with: + * @code{.cpp} + * void(Event &); * @endcode * * The order of invocation of the listeners isn't guaranteed. @@ -15933,7 +47644,7 @@ * @return A temporary sink object. */ template - sink_type sink() ENTT_NOEXCEPT { + [[nodiscard]] auto sink() { return assure().sink(); } @@ -15996,6 +47707,53 @@ } /** + * @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 @@ -16017,42 +47775,41 @@ * to reduce at a minimum the time spent in the bodies of the listeners. */ void update() const { - for(auto pos = wrappers.size(); pos; --pos) { - auto &wdata = wrappers[pos-1]; - - if(wdata.wrapper) { - wdata.wrapper->publish(); - } - } - } - -private: - std::vector wrappers; -}; - - -} - - -#endif // ENTT_SIGNAL_DISPATCHER_HPP + 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 +#include #include -#include -// #include "../config/config.h" - -// #include "../core/family.hpp" - -// #include "../core/type_traits.hpp" +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" @@ -16071,33 +47828,33 @@ * } * @endcode * - * Handlers for the type of events are created internally on the fly. It's not + * 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 const 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. + * 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 { - using handler_family = family; - - struct base_handler { - virtual ~base_handler() = default; + struct basic_pool { + virtual ~basic_pool() = default; virtual bool empty() const ENTT_NOEXCEPT = 0; virtual void clear() ENTT_NOEXCEPT = 0; }; template - struct event_handler: base_handler { - using listener_type = std::function; + 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; - bool empty() const ENTT_NOEXCEPT override { + [[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) && @@ -16106,9 +47863,13 @@ void clear() ENTT_NOEXCEPT override { if(publishing) { - auto func = [](auto &&element) { element.first = true; }; - std::for_each(once_list.begin(), once_list.end(), func); - std::for_each(on_list.begin(), on_list.end(), func); + for(auto &&element: once_list) { + element.first = true; + } + + for(auto &&element: on_list) { + element.first = true; + } } else { once_list.clear(); on_list.clear(); @@ -16123,7 +47884,7 @@ return on_list.emplace(on_list.cend(), false, std::move(listener)); } - void erase(connection_type conn) ENTT_NOEXCEPT { + void erase(connection_type conn) { conn->first = true; if(!publishing) { @@ -16133,18 +47894,19 @@ } } - void publish(const Event &event, Derived &ref) { + void publish(Event &event, Derived &ref) { container_type swap_list; once_list.swap(swap_list); - auto func = [&event, &ref](auto &&element) { - return element.first ? void() : element.second(event, ref); - }; - publishing = true; - std::for_each(on_list.rbegin(), on_list.rend(), func); - std::for_each(swap_list.rbegin(), swap_list.rend(), func); + 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; @@ -16157,57 +47919,31 @@ container_type on_list{}; }; - struct handler_data { - std::unique_ptr handler; - ENTT_ID_TYPE runtime_type; - }; - template - static auto type() ENTT_NOEXCEPT { - if constexpr(is_named_type_v) { - return named_type_traits::value; - } else { - return handler_family::type; - } + [[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 - event_handler * assure() const ENTT_NOEXCEPT { - const auto htype = type(); - handler_data *hdata = nullptr; - - if constexpr(is_named_type_v) { - const auto it = std::find_if(handlers.begin(), handlers.end(), [htype](const auto &candidate) { - return candidate.handler && candidate.runtime_type == htype; - }); - - hdata = (it == handlers.cend() ? &handlers.emplace_back() : &(*it)); - } else { - if(!(htype < handlers.size())) { - handlers.resize(htype+1); - } - - hdata = &handlers[htype]; - - if(hdata->handler && hdata->runtime_type != htype) { - handlers.emplace_back(); - std::swap(handlers[htype], handlers.back()); - hdata = &handlers[htype]; - } - } - - if(!hdata->handler) { - hdata->handler = std::make_unique>(); - hdata->runtime_type = htype; - } - - return static_cast *>(hdata->handler.get()); + [[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 event_handler::listener_type; + using listener = typename pool_handler::listener_type; /** * @brief Generic connection type for events. @@ -16219,28 +47955,28 @@ * @tparam Event Type of event for which the connection is created. */ template - struct connection: private event_handler::connection_type { + struct connection: private pool_handler::connection_type { /** @brief Event emitters are friend classes of connections. */ friend class emitter; /*! @brief Default constructor. */ - connection() ENTT_NOEXCEPT = default; + connection() = default; /** * @brief Creates a connection that wraps its underlying instance. * @param conn A connection object to wrap. */ - connection(typename event_handler::connection_type conn) - : event_handler::connection_type{std::move(conn)} + connection(typename pool_handler::connection_type conn) + : pool_handler::connection_type{std::move(conn)} {} }; /*! @brief Default constructor. */ - emitter() ENTT_NOEXCEPT = default; + emitter() = default; /*! @brief Default destructor. */ - virtual ~emitter() ENTT_NOEXCEPT { - static_assert(std::is_base_of_v, Derived>); + virtual ~emitter() { + static_assert(std::is_base_of_v, Derived>, "Incorrect use of the class template"); } /*! @brief Default move constructor. */ @@ -16262,7 +47998,8 @@ */ template void publish(Args &&... args) { - assure()->publish({ std::forward(args)... }, *static_cast(this)); + Event instance{std::forward(args)...}; + assure()->publish(instance, *static_cast(this)); } /** @@ -16274,7 +48011,7 @@ * 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 `void(const Event &, Derived &)`. + * which is _compatible_ with `void(Event &, Derived &)`. * * @note * Whenever an event is emitted, the emitter provides the listener with a @@ -16299,7 +48036,7 @@ * 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 `void(const Event &, Derived &)`. + * which is _compatible_ with `void(Event &, Derived &)`. * * @note * Whenever an event is emitted, the emitter provides the listener with a @@ -16325,7 +48062,7 @@ * @param conn A valid connection. */ template - void erase(connection conn) ENTT_NOEXCEPT { + void erase(connection conn) { assure()->erase(std::move(conn)); } @@ -16338,7 +48075,7 @@ * @tparam Event Type of event to reset. */ template - void clear() ENTT_NOEXCEPT { + void clear() { assure()->clear(); } @@ -16349,9 +48086,11 @@ * results in undefined behavior. */ void clear() ENTT_NOEXCEPT { - std::for_each(handlers.begin(), handlers.end(), [](auto &&hdata) { - return hdata.handler ? hdata.handler->clear() : void(); - }); + for(auto &&cpool: pools) { + if(cpool) { + cpool->clear(); + } + } } /** @@ -16360,29 +48099,553 @@ * @return True if there are no listeners registered, false otherwise. */ template - bool empty() const ENTT_NOEXCEPT { - return assure()->empty(); + [[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. */ - bool empty() const ENTT_NOEXCEPT { - return std::all_of(handlers.cbegin(), handlers.cend(), [](auto &&hdata) { - return !hdata.handler || hdata.handler->empty(); + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { + return !cpool || cpool->empty(); }); } private: - mutable std::vector handlers{}; -}; - - -} - - -#endif // ENTT_SIGNAL_EMITTER_HPP + 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 +