diff --git a/single_include/entt/entt.hpp b/single_include/entt/entt.hpp index f2389b129..593005629 100644 --- a/single_include/entt/entt.hpp +++ b/single_include/entt/entt.hpp @@ -40,6 +40,15 @@ #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -102,6 +111,60 @@ template constexpr auto overload(Type *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(Type...) -> 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; +}; + + } @@ -264,11 +327,7 @@ namespace entt { */ 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++; + inline static ENTT_MAYBE_ATOMIC(ENTT_ID_TYPE) identifier{}; public: /*! @brief Unsigned integer type. */ @@ -277,7 +336,7 @@ public: /*! @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...>; + inline static const family_type type = identifier++; }; @@ -600,7 +659,7 @@ public: /*! @brief Statically generated unique identifier for the given type. */ template - static constexpr identifier_type type = get>(std::make_index_sequence{}); + static constexpr identifier_type type = get>(std::index_sequence_for{}); }; @@ -679,6 +738,7 @@ inline monostate monostate_v = {}; #define ENTT_CORE_TYPE_TRAITS_HPP +#include #include // #include "../config/config.h" @@ -716,6 +776,15 @@ inline monostate monostate_v = {}; #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -990,6 +1059,32 @@ constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, st 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 TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +constexpr choice_t choice{}; + + /*! @brief A class to use to push around lists of types, nothing more. */ template struct type_list {}; @@ -1100,6 +1195,28 @@ template using type_list_unique_t = typename type_list_unique::type; +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + /*! @brief Traits class used mainly to push things across boundaries. */ template struct named_type_traits; @@ -1124,11 +1241,11 @@ 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. + * @brief Helper variable template. + * @tparam Type Potentially named type. */ -template> -struct is_named_type: std::false_type {}; +template +constexpr auto named_type_traits_v = named_type_traits::value; /** @@ -1136,6 +1253,11 @@ struct is_named_type: std::false_type {}; * name. In all other cases, `value` is false. * @tparam Type Potentially named type. */ +template> +struct is_named_type: std::false_type {}; + + +/*! @copydoc is_named_type */ template struct is_named_type>>>: std::true_type {}; @@ -1158,7 +1280,8 @@ constexpr auto is_named_type_v = is_named_type::value; enum class clazz: type {};\ constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ return std::underlying_type_t(id);\ - } + }\ + static_assert(true) } @@ -1196,8 +1319,9 @@ constexpr auto is_named_type_v = is_named_type::value; struct entt::named_type_traits\ : std::integral_constant>>>{#type}>\ {\ - static_assert(std::is_same_v, type>);\ - }; + static_assert(std::is_same_v, type>);\ + static_assert(std::is_object_v);\ + } /** @@ -1294,6 +1418,15 @@ constexpr auto is_named_type_v = is_named_type::value; #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -1364,6 +1497,15 @@ constexpr auto is_named_type_v = is_named_type::value; #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -1399,11 +1541,7 @@ namespace entt { */ 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++; + inline static ENTT_MAYBE_ATOMIC(ENTT_ID_TYPE) identifier{}; public: /*! @brief Unsigned integer type. */ @@ -1412,7 +1550,7 @@ public: /*! @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...>; + inline static const family_type type = identifier++; }; @@ -1478,6 +1616,60 @@ template constexpr auto overload(Type *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(Type...) -> 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; +}; + + } @@ -1623,6 +1815,7 @@ struct radix_sort { #define ENTT_CORE_TYPE_TRAITS_HPP +#include #include // #include "../config/config.h" @@ -1660,6 +1853,15 @@ struct radix_sort { #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -1934,6 +2136,32 @@ constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, st 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 TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +constexpr choice_t choice{}; + + /*! @brief A class to use to push around lists of types, nothing more. */ template struct type_list {}; @@ -2044,6 +2272,28 @@ template using type_list_unique_t = typename type_list_unique::type; +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + /*! @brief Traits class used mainly to push things across boundaries. */ template struct named_type_traits; @@ -2068,11 +2318,11 @@ 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. + * @brief Helper variable template. + * @tparam Type Potentially named type. */ -template> -struct is_named_type: std::false_type {}; +template +constexpr auto named_type_traits_v = named_type_traits::value; /** @@ -2080,6 +2330,11 @@ struct is_named_type: std::false_type {}; * name. In all other cases, `value` is false. * @tparam Type Potentially named type. */ +template> +struct is_named_type: std::false_type {}; + + +/*! @copydoc is_named_type */ template struct is_named_type>>>: std::true_type {}; @@ -2102,7 +2357,8 @@ constexpr auto is_named_type_v = is_named_type::value; enum class clazz: type {};\ constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ return std::underlying_type_t(id);\ - } + }\ + static_assert(true) } @@ -2140,8 +2396,9 @@ constexpr auto is_named_type_v = is_named_type::value; struct entt::named_type_traits\ : std::integral_constant>>>{#type}>\ {\ - static_assert(std::is_same_v, type>);\ - }; + static_assert(std::is_same_v, type>);\ + static_assert(std::is_object_v);\ + } /** @@ -2239,6 +2496,15 @@ constexpr auto is_named_type_v = is_named_type::value; #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -2278,34 +2544,34 @@ 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(*)(Type &, Args...), const Payload *) -> Ret(*)(Args...); + + +template>> +auto to_function_pointer(Ret(*)(Type *, Args...), const Payload *) -> Ret(*)(Args...); template -auto to_function_pointer(Ret(Class:: *)(Args...), const Class &) -> Ret(*)(Args...); +auto to_function_pointer(Ret(Class:: *)(Args...), const Class *) -> Ret(*)(Args...); template -auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class &) -> Ret(*)(Args...); +auto to_function_pointer(Ret(Class:: *)(Args...) const, const Class *) -> Ret(*)(Args...); template -auto to_function_pointer(Type Class:: *, const Class &) -> Type(*)(); +auto to_function_pointer(Type Class:: *, const Class *) -> Type(*)(); -template -struct function_extent; +template +using to_function_pointer_t = decltype(internal::to_function_pointer(std::declval()...)); template -struct function_extent { - static constexpr auto value = sizeof...(Args); -}; - - -template -constexpr auto function_extent_v = function_extent::value; +constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} } @@ -2371,19 +2637,24 @@ class delegate { 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)); - } - + Type *curr = static_cast(const_cast, const void *, void *>>(payload)); // Ret(...) makes void(...) eat the return values to avoid errors return Ret(std::invoke(Candidate, *curr, 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 = static_cast(const_cast, const void *, void *>>(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...); @@ -2418,14 +2689,27 @@ public: connect(value_or_instance); } + /** + * @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 pointer 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{}); + connect(internal::index_sequence_for(internal::to_function_pointer_t{})); } /** @@ -2445,8 +2729,27 @@ public: */ 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{}); + connect(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t{})); + } + + /** + * @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 pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + connect(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t{})); } /** @@ -2534,7 +2837,7 @@ bool operator!=(const delegate &lhs, const delegate */ template delegate(connect_arg_t) ENTT_NOEXCEPT --> delegate>; +-> delegate>>; /** @@ -2543,13 +2846,26 @@ delegate(connect_arg_t) ENTT_NOEXCEPT * 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>; +delegate(connect_arg_t, Type &) 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. + * + * @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 *) ENTT_NOEXCEPT +-> delegate>>; } @@ -2562,9 +2878,10 @@ delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT #define ENTT_SIGNAL_SIGH_HPP -#include -#include #include +#include +#include +#include #include #include // #include "../config/config.h" @@ -2576,10 +2893,6 @@ delegate(connect_arg_t, Type &value_or_instance) ENTT_NOEXCEPT #define ENTT_SIGNAL_FWD_HPP -// #include "../config/config.h" - - - namespace entt { @@ -2652,7 +2965,7 @@ class sigh { 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; @@ -2687,9 +3000,9 @@ public: * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { - for(auto pos = calls.size(); pos; --pos) { - calls[pos-1](args...); - } + std::for_each(calls.cbegin(), calls.cend(), [&args...](auto &&call) { + call(args...); + }); } /** @@ -2708,22 +3021,20 @@ public: */ 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...)); } } } @@ -2818,6 +3129,9 @@ private: * when it goes out of scope. */ struct scoped_connection: private connection { + using connection::operator bool; + using connection::release; + /*! @brief Default constructor. */ scoped_connection() = default; @@ -2871,9 +3185,6 @@ struct scoped_connection: private connection { static_cast(*this) = std::move(other); return *this; } - - using connection::operator bool; - using connection::release; }; @@ -2894,9 +3205,10 @@ struct scoped_connection: private connection { 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); } @@ -2911,7 +3223,8 @@ public: * @param ref A valid reference to a signal object. */ sink(sigh &ref) ENTT_NOEXCEPT - : signal{&ref} + : offset{}, + signal{&ref} {} /** @@ -2922,6 +3235,111 @@ public: return signal->calls.empty(); } + /** + * @brief Returns a sink that connects before a given function. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + 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 given member function or + * free function with payload. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + * @return A properly initialized sink object. + */ + template + 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 member function or + * free function with payload. + * @tparam Candidate Member or free function to look for. + * @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 + 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 + 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 + 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. + */ + sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + /** * @brief Connects a free function to a signal. * @@ -2934,9 +3352,13 @@ public: 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>(); - signal->calls.emplace_back(delegate{connect_arg}); return { std::move(conn), signal }; } @@ -2960,9 +3382,43 @@ public: 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<&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 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 pointer 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 }; } @@ -2973,9 +3429,9 @@ public: template void disconnect() { auto &calls = signal->calls; - delegate delegate{}; - delegate.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** @@ -2988,23 +3444,51 @@ public: 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()); + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), 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 pointer 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 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. + * @param value_or_instance A valid object 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()); + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @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 pointer 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. */ @@ -3013,6 +3497,7 @@ public: } private: + difference_type offset; signal_type *signal; }; @@ -3247,7 +3732,6 @@ constexpr auto null = internal::null{}; #define ENTT_ENTITY_FWD_HPP -#include // #include "../config/config.h" // #include "../core/type_traits.hpp" @@ -3261,7 +3745,7 @@ template class basic_registry; /*! @class basic_view */ -template +template class basic_view; /*! @class basic_runtime_view */ @@ -3276,14 +3760,10 @@ class basic_group; template class basic_observer; -/*! @class basic_actor */ +/*! @struct basic_actor */ template struct basic_actor; -/*! @class basic_prototype */ -template -class basic_prototype; - /*! @class basic_snapshot */ template class basic_snapshot; @@ -3297,10 +3777,10 @@ template class basic_continuous_loader; /*! @brief Alias declaration for the most common use case. */ -ENTT_OPAQUE_TYPE(entity, ENTT_ID_TYPE) +ENTT_OPAQUE_TYPE(entity, ENTT_ID_TYPE); /*! @brief Alias declaration for the most common use case. */ -ENTT_OPAQUE_TYPE(component, ENTT_ID_TYPE) +ENTT_OPAQUE_TYPE(component, ENTT_ID_TYPE); /*! @brief Alias declaration for the most common use case. */ using registry = basic_registry; @@ -3311,9 +3791,6 @@ 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; @@ -3325,7 +3802,7 @@ using continuous_loader = basic_continuous_loader; /** * @brief Alias declaration for the most common use case. - * @tparam Component Types of components iterated by the view. + * @tparam Types Types of components iterated by the view. */ template using view = basic_view; @@ -3794,16 +4271,16 @@ public: * 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. + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. */ - 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]); + virtual void swap(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT { + auto [src_page, src_offset] = map(lhs); + auto [dst_page, dst_offset] = map(rhs); + auto &from = reverse[src_page][src_offset]; + auto &to = reverse[dst_page][dst_offset]; + std::swap(direct[size_type(from)], direct[size_type(to)]); + std::swap(from, to); } /** @@ -3831,14 +4308,10 @@ public: * * 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. + * `data` 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. @@ -3851,25 +4324,72 @@ public: */ template void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(first > last)); + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); - std::vector copy(last - first); - const auto offset = std::distance(last, end()); - std::iota(copy.begin(), copy.end(), size_type{}); + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = direct.rend() - skip; + const auto from = to - length; - 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)...); + algo(from, to, std::move(compare), std::forward(args)...); - for(size_type pos{}, length = copy.size(); pos < length; ++pos) { + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { + auto [page, offset] = map(direct[pos]); + reverse[page][offset] = entity_type(pos); + } + } + + /** + * @brief Sort elements according to the given comparison function. + * + * @sa sort + * + * This function is a slightly slower version of `sort` that invokes the + * caller to indicate which entities are swapped.
+ * It's recommended when the caller wants to sort its own data structures to + * align them with the order induced in the sparse set. + * + * The signature of the callback should be equivalent to the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * @endcode + * + * @tparam Apply Type of function object to invoke to notify the caller. + * @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 apply A valid function object to use as a callback. + * @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 arrange(iterator_type first, iterator_type last, Apply apply, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto length = std::distance(first, last); + const auto skip = std::distance(last, end()); + const auto to = direct.rend() - skip; + const auto from = to - length; + + algo(from, to, std::move(compare), std::forward(args)...); + + for(size_type pos = skip, end = skip+length; pos < end; ++pos) { auto curr = pos; - auto next = copy[curr]; + auto next = index(direct[curr]); while(curr != next) { - swap(copy[curr] + offset, copy[next] + offset); - copy[curr] = curr; + apply(direct[curr], direct[next]); + auto [page, offset] = map(direct[curr]); + reverse[page][offset] = entity_type(curr); + curr = next; - next = copy[curr]; + next = index(direct[curr]); } } } @@ -3888,8 +4408,9 @@ public: * 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. + * Attempting to iterate elements using a raw pointer returned by a call to + * `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. */ @@ -3902,7 +4423,7 @@ public: while(pos && from != to) { if(has(*from)) { if(*from != direct[pos]) { - swap(pos, index(*from)); + swap(direct[pos], *from); } --pos; @@ -3984,7 +4505,6 @@ class basic_runtime_view { 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; @@ -4322,25 +4842,7 @@ public: */ 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)); - } - }; - } else { - (component(archive), ...); - } - + (component(archive, reg->template data(), reg->template data() + reg->template size()), ...); return *this; } @@ -4360,7 +4862,7 @@ public: */ template const basic_snapshot & component(Archive &archive, It first, It last) const { - component(archive, first, last, std::make_index_sequence{}); + component(archive, first, last, std::index_sequence_for{}); return *this; } @@ -4558,6 +5060,40 @@ class basic_continuous_loader { } } + 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); + 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); + + for(auto &&entt: container) { + entt = map(entt); + } + } + template void update(Other &instance, Member Type:: *member) { if constexpr(!std::is_same_v) { @@ -4566,9 +5102,7 @@ class basic_continuous_loader { instance.*member = map(instance.*member); } else { // maybe a container? let's try... - for(auto &entt: instance.*member) { - entt = map(entt); - } + update(0, instance.*member); } } @@ -5117,9 +5651,10 @@ public: /** * @brief Assigns one or more entities to a storage and default constructs - * their objects. + * or copy constructs their objects. * - * The object type must be at least move and default insertable. + * The object type must be at least move and default insertable if no + * arguments are provided, move and copy insertable otherwise. * * @warning * Attempting to assign an entity that already belongs to the storage @@ -5128,37 +5663,21 @@ public: * storage already contains the given entity. * * @tparam It Type of forward iterator. + * @tparam Args Types of arguments to use to construct the object. * @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 construct an object for the 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(); - } + template + iterator_type batch(It first, It last, Args &&... args) { + if constexpr(sizeof...(Args) == 0) { + instances.resize(instances.size() + std::distance(first, last)); + } else { + instances.resize(instances.size() + std::distance(first, last), Type{std::forward(args)...}); + } - /** - * @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. - */ - 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(); @@ -5191,13 +5710,11 @@ public: * 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. + * @param lhs A valid entity identifier. + * @param rhs A valid entity identifier. */ - 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]); + void swap(const entity_type lhs, const entity_type rhs) ENTT_NOEXCEPT override { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); underlying_type::swap(lhs, rhs); } @@ -5227,15 +5744,16 @@ public: * * 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. @@ -5247,19 +5765,22 @@ public: */ template void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) { - ENTT_ASSERT(!(first > last)); + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); 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); + const auto apply = [this](const auto lhs, const auto rhs) { + std::swap(instances[underlying_type::index(lhs)], instances[underlying_type::index(rhs)]); + }; - underlying_type::sort(from, to, [this, compare = std::move(compare)](const auto lhs, const auto rhs) { + if constexpr(std::is_invocable_v) { + underlying_type::arrange(from, to, std::move(apply), [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)...); + underlying_type::arrange(from, to, std::move(apply), std::move(compare), std::move(algo), std::forward(args)...); } } @@ -5276,7 +5797,7 @@ private: /*! @copydoc basic_storage */ template -class basic_storage>>: public sparse_set { +class basic_storage>: public sparse_set { using traits_type = entt_traits>; using underlying_type = sparse_set; @@ -5475,6 +5996,18 @@ public: underlying_type::batch(first, last); return begin(); } + + /*! @copydoc storage::sort */ + template + void sort(iterator_type first, iterator_type last, Compare compare, Sort algo = Sort{}, Args &&... args) { + ENTT_ASSERT(!(last < first)); + ENTT_ASSERT(!(last > end())); + + const auto from = underlying_type::begin() + std::distance(begin(), first); + const auto to = from + std::distance(first, last); + + underlying_type::sort(from, to, std::move(compare), std::move(algo), std::forward(args)...); + } }; /*! @copydoc basic_storage */ @@ -5610,11 +6143,10 @@ class basic_group; * * @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. + * @tparam Get Type of components observed by the group. */ -template -class basic_group, get_t> { +template +class basic_group, get_t> { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; @@ -5622,9 +6154,9 @@ class basic_group, get_t> { 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 + basic_group(sparse_set *ref, storage> *... gpool) ENTT_NOEXCEPT : handler{ref}, - pools{get, other...} + pools{gpool...} {} template @@ -5635,14 +6167,14 @@ class basic_group, get_t> { } else { func(entt, std::get *>(pools)->get(entt)...); } - }; + } } 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; + using size_type = std::size_t; /*! @brief Input iterator type. */ using iterator_type = typename sparse_set::iterator_type; @@ -5679,22 +6211,19 @@ public: } /** - * @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. + * @brief Checks whether the group or the pools of the given components are + * empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools of the given components are empty, + * false otherwise. */ - template + 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(); + if constexpr(sizeof...(Component) == 0) { + return handler->empty(); + } else { + return (std::get *>(pools)->empty() && ...); + } } /** @@ -5841,7 +6370,7 @@ public: if constexpr(sizeof...(Component) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple(entt))...>{get(entt)...}; + return std::tuple({}))...>{get(entt)...}; } } @@ -5856,8 +6385,8 @@ public: * forms: * * @code{.cpp} - * void(const entity_type, Get &, Other &...); - * void(Get &, Other &...); + * void(const entity_type, Get &...); + * void(Get &...); * @endcode * * @note @@ -5870,7 +6399,7 @@ public: */ template void each(Func func) const { - traverse(std::move(func), type_list{}); + traverse(std::move(func), type_list{}); } /** @@ -5895,9 +6424,8 @@ public: */ 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{}); + using get_type_list = type_list_cat_t, type_list>...>; + traverse(std::move(func), get_type_list{}); } /** @@ -5928,10 +6456,6 @@ public: * * 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 @@ -5950,10 +6474,13 @@ public: 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 if constexpr(sizeof...(Component) == 1) { + handler->sort(handler->begin(), handler->end(), [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(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)); + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); }, std::move(algo), std::forward(args)...); } } @@ -5981,7 +6508,7 @@ public: private: sparse_set *handler; - const std::tuple *, pool_type *...> pools; + const std::tuple *...> pools; }; @@ -6030,11 +6557,10 @@ private: * @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. + * @tparam Owned Types of components owned by the group. */ -template -class basic_group, get_t, Owned, Other...> { +template +class basic_group, get_t, Owned...> { /*! @brief A registry is allowed to create groups. */ friend class basic_registry; @@ -6045,36 +6571,37 @@ class basic_group, get_t, Owned, Other...> 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...} + basic_group(const std::size_t *ref, const std::size_t *extent, storage> *... opool, storage> *... gpool) ENTT_NOEXCEPT + : pools{opool..., gpool...}, + length{extent}, + super{ref} {} 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; + [[maybe_unused]] auto it = std::make_tuple((std::get *>(pools)->end() - *length)...); + [[maybe_unused]] auto data = std::get<0>(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)++)...); + func(*(std::get>(it)++)...); } else { const auto entt = *(data++); - func(*(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); + func(*(std::get>(it)++)..., std::get *>(pools)->get(entt)...); } } else { const auto entt = *(data++); - func(entt, *(std::get>(raw)++)..., std::get *>(pools)->get(entt)...); + func(entt, *(std::get>(it)++)..., std::get *>(pools)->get(entt)...); } } } 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; + using size_type = std::size_t; /*! @brief Input iterator type. */ using iterator_type = typename sparse_set::iterator_type; @@ -6097,22 +6624,19 @@ public: } /** - * @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. + * @brief Checks whether the group or the pools of the given components are + * empty. + * @tparam Component Types of components in which one is interested. + * @return True if the group or the pools of the given components are empty, + * false otherwise. */ - template + 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; + if constexpr(sizeof...(Component) == 0) { + return !*length; + } else { + return (std::get *>(pools)->empty() && ...); + } } /** @@ -6172,7 +6696,7 @@ public: * @return A pointer to the array of entities. */ const entity_type * data() const ENTT_NOEXCEPT { - return std::get *>(pools)->data(); + return std::get<0>(pools)->data(); } /** @@ -6190,7 +6714,7 @@ public: * @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; + return std::get<0>(pools)->sparse_set::end() - *length; } /** @@ -6209,7 +6733,7 @@ public: * given components. */ iterator_type end() const ENTT_NOEXCEPT { - return std::get *>(pools)->sparse_set::end(); + return std::get<0>(pools)->sparse_set::end(); } /** @@ -6219,7 +6743,7 @@ public: * iterator otherwise. */ iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { - const auto it = std::get *>(pools)->find(entt); + const auto it = std::get<0>(pools)->find(entt); return it != end() && it >= begin() && *it == entt ? it : end(); } @@ -6265,7 +6789,7 @@ public: if constexpr(sizeof...(Component) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple(entt))...>{get(entt)...}; + return std::tuple({}))...>{get(entt)...}; } } @@ -6280,8 +6804,8 @@ public: * forms: * * @code{.cpp} - * void(const entity_type, Owned &, Other &..., Get &...); - * void(Owned &, Other &..., Get &...); + * void(const entity_type, Owned &..., Get &...); + * void(Owned &..., Get &...); * @endcode * * @note @@ -6294,7 +6818,7 @@ public: */ template void each(Func func) const { - traverse(std::move(func), type_list{}, type_list{}); + traverse(std::move(func), type_list{}, type_list{}); } /** @@ -6319,10 +6843,18 @@ public: */ 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{}); + using owned_type_list = type_list_cat_t, type_list>...>; + using get_type_list = type_list_cat_t, type_list>...>; + traverse(std::move(func), owned_type_list{}, get_type_list{}); + } + + /** + * @brief Checks whether the group can be sorted. + * @return True if the group can be sorted, false otherwise. + */ + bool sortable() const ENTT_NOEXCEPT { + constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + return *super == size; } /** @@ -6354,10 +6886,6 @@ public: * * 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 @@ -6373,28 +6901,35 @@ public: */ template void sort(Compare compare, Sort algo = Sort{}, Args &&... args) { - auto *cpool = std::get *>(pools); + ENTT_ASSERT(sortable()); + auto *cpool = std::get<0>(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 if constexpr(sizeof...(Component) == 1) { + cpool->sort(cpool->end()-*length, cpool->end(), [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(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)); + return compare(std::tuple({}))...>{std::get *>(pools)->get(lhs)...}, std::tuple({}))...>{std::get *>(pools)->get(rhs)...}); }, std::move(algo), std::forward(args)...); } - for(auto next = *length; next; --next) { - const auto pos = next - 1; - const auto entt = cpool->data()[pos]; - (std::get *>(pools)->swap(pos, std::get *>(pools)->index(entt)), ...); - } + [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 typename basic_registry::size_type *length; - const std::tuple *, pool_type *..., pool_type *...> pools; + const std::tuple *..., pool_type *...> pools; + const size_type *length; + const size_type *super; }; @@ -6422,6 +6957,8 @@ private: // #include "storage.hpp" +// #include "utility.hpp" + // #include "entity.hpp" // #include "fwd.hpp" @@ -6431,6 +6968,16 @@ private: namespace entt { +/** + * @brief View. + * + * 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; + + /** * @brief Multi component view. * @@ -6440,8 +6987,7 @@ namespace entt { * 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. + * underlying data structures. See sparse_set for more details. * * @b Important * @@ -6466,12 +7012,11 @@ namespace entt { * 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 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); - +template +class basic_view, Component...> { /*! @brief A registry is allowed to create views. */ friend class basic_registry; @@ -6483,15 +7028,16 @@ class basic_view { using underlying_iterator_type = typename sparse_set::iterator_type; using unchecked_type = std::array *, (sizeof...(Component) - 1)>; - using traits_type = entt_traits>; + using filter_type = std::array *, sizeof...(Exclude)>; class iterator { - friend class basic_view; + friend class basic_view, Component...>; - iterator(unchecked_type other, underlying_iterator_type first, underlying_iterator_type last) ENTT_NOEXCEPT - : unchecked{other}, - begin{first}, - end{last} + iterator(underlying_iterator_type first, underlying_iterator_type last, unchecked_type other, filter_type ignore) ENTT_NOEXCEPT + : begin{first}, + end{last}, + unchecked{other}, + filter{ignore} { if(begin != end && !valid()) { ++(*this); @@ -6499,9 +7045,8 @@ class basic_view { } bool valid() const ENTT_NOEXCEPT { - return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set *view) { - return view->has(*begin); - }); + return std::all_of(unchecked.cbegin(), unchecked.cend(), [this](const sparse_set *view) { return view->has(*begin); }) + && std::none_of(filter.cbegin(), filter.cend(), [this](const sparse_set *view) { return view->has(*begin); }); } public: @@ -6539,14 +7084,16 @@ class basic_view { } private: - unchecked_type unchecked; underlying_iterator_type begin; underlying_iterator_type end; + unchecked_type unchecked; + filter_type filter; }; // 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...} + basic_view(storage> *... component, storage> *... epool) ENTT_NOEXCEPT + : pools{component...}, + filter{epool...} {} const sparse_set * candidate() const ENTT_NOEXCEPT { @@ -6556,8 +7103,8 @@ class basic_view { } unchecked_type unchecked(const sparse_set *view) const ENTT_NOEXCEPT { + std::size_t pos{}; unchecked_type other{}; - typename unchecked_type::size_type pos{}; ((std::get *>(pools) == view ? nullptr : (other[pos++] = std::get *>(pools))), ...); return other; } @@ -6580,7 +7127,7 @@ class basic_view { 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((std::get *>(pools)->has(entity) && ...) && (!std::get *>(filter)->has(entity) && ...)) { if constexpr(std::is_invocable_v({}))...>) { func(get(curr, std::get *>(pools), entity)...); } else { @@ -6589,8 +7136,8 @@ class basic_view { } }); } else { - std::for_each(begin, end, [this, &func](const auto entity) mutable { - if((std::get *>(pools)->has(entity) && ...)) { + std::for_each(begin, end, [this, &func](const auto entity) { + if((std::get *>(pools)->has(entity) && ...) && (!std::get *>(filter)->has(entity) && ...)) { if constexpr(std::is_invocable_v({}))...>) { func(std::get *>(pools)->get(entity)...); } else { @@ -6611,6 +7158,9 @@ public: /** * @brief Returns the number of existing components of the given type. + * + * This isn't the number of entities iterated by the view. + * * @tparam Comp Type of component of which to return the size. * @return Number of existing components of the given type. */ @@ -6620,30 +7170,32 @@ public: } /** - * @brief Estimates the number of entities that have the given components. - * @return Estimated number of entities that have the given components. + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. */ 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. + * @brief Checks whether the view or the pools of the given components are + * empty. + * + * The view is definitely empty if one of the pools of the given components + * is empty. In all other cases, the view may be empty and not return + * entities even if this function returns false. + * + * @tparam Comp Types of components in which one is interested. + * @return True if the view or the pools of the given components are empty, + * false otherwise. */ - template + 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() || ...); + if constexpr(sizeof...(Comp) == 0) { + return (std::get *>(pools)->empty() || ...); + } else { + return (std::get *>(pools)->empty() && ...); + } } /** @@ -6700,7 +7252,8 @@ public: */ iterator_type begin() const ENTT_NOEXCEPT { const auto *view = candidate(); - return iterator_type{unchecked(view), view->begin(), view->end()}; + const filter_type ignore{std::get *>(filter)...}; + return iterator_type{view->begin(), view->end(), unchecked(view), ignore}; } /** @@ -6720,7 +7273,8 @@ public: */ iterator_type end() const ENTT_NOEXCEPT { const auto *view = candidate(); - return iterator_type{unchecked(view), view->end(), view->end()}; + const filter_type ignore{std::get *>(filter)...}; + return iterator_type{view->end(), view->end(), unchecked(view), ignore}; } /** @@ -6731,7 +7285,8 @@ public: */ iterator_type find(const entity_type entt) const ENTT_NOEXCEPT { const auto *view = candidate(); - iterator_type it{unchecked(view), view->find(entt), view->end()}; + const filter_type ignore{std::get *>(filter)...}; + iterator_type it{view->find(entt), view->end(), unchecked(view), ignore}; return (it != end() && *it == entt) ? it : end(); } @@ -6765,10 +7320,13 @@ public: decltype(auto) get([[maybe_unused]] const entity_type entt) const ENTT_NOEXCEPT { ENTT_ASSERT(contains(entt)); - if constexpr(sizeof...(Comp) == 1) { + if constexpr(sizeof...(Comp) == 0) { + static_assert(sizeof...(Component) == 1); + return (std::get *>(pools)->get(entt), ...); + } else if constexpr(sizeof...(Comp) == 1) { return (std::get *>(pools)->get(entt), ...); } else { - return std::tuple(entt))...>{get(entt)...}; + return std::tuple({}))...>{get(entt)...}; } } @@ -6893,12 +7451,13 @@ public: 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>...>; + using non_empty_type = type_list_cat_t, type_list>...>; traverse(std::move(func), other_type{}, non_empty_type{}); } private: const std::tuple *...> pools; + const std::tuple *...> filter; }; @@ -6909,8 +7468,7 @@ private: * 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. + * underlying data structure. See sparse_set for more details. * * @b Important * @@ -6937,7 +7495,7 @@ private: * @tparam Component Type of component iterated by the view. */ template -class basic_view { +class basic_view, Component> { /*! @brief A registry is allowed to create views. */ friend class basic_registry; @@ -7086,7 +7644,9 @@ public: * @param entt A valid entity identifier. * @return The component assigned to the entity. */ + template decltype(auto) get(const entity_type entt) const ENTT_NOEXCEPT { + static_assert(std::is_same_v); ENTT_ASSERT(contains(entt)); return pool->get(entt); } @@ -7155,7 +7715,7 @@ public: */ template void less(Func func) const { - if constexpr(std::is_empty_v) { + if constexpr(ENTT_ENABLE_ETO(Component)) { if constexpr(std::is_invocable_v) { for(auto pos = pool->size(); pos; --pos) { func(); @@ -7201,13 +7761,9 @@ class basic_registry { using component_family = family; using traits_type = entt_traits>; - struct group_type { - std::size_t owned{}; - }; - template struct pool_handler: storage { - group_type *group{}; + std::size_t super{}; pool_handler() ENTT_NOEXCEPT = default; @@ -7228,51 +7784,51 @@ class basic_registry { } template - decltype(auto) assign(basic_registry ®istry, const Entity entt, Args &&... args) { - if constexpr(std::is_empty_v) { + decltype(auto) assign(basic_registry &owner, const Entity entt, Args &&... args) { + if constexpr(ENTT_ENABLE_ETO(Component)) { storage::construct(entt); - construction.publish(entt, registry, Component{}); + construction.publish(entt, owner, Component{}); return Component{std::forward(args)...}; } else { auto &component = storage::construct(entt, std::forward(args)...); - construction.publish(entt, registry, component); + construction.publish(entt, owner, component); return component; } } - template - auto batch(basic_registry ®istry, It first, It last, const Comp &... value) { - auto it = storage::batch(first, last, value...); + template + auto batch(basic_registry &owner, It first, It last, Args &&... args) { + auto it = storage::batch(first, last, std::forward(args)...); if(!construction.empty()) { - std::for_each(first, last, [this, ®istry, it](const auto entt) mutable { - construction.publish(entt, registry, *(it++)); + std::for_each(first, last, [this, &owner, it](const auto entt) mutable { + construction.publish(entt, owner, *(it++)); }); } return it; } - void remove(basic_registry ®istry, const Entity entt) { - destruction.publish(entt, registry); + void remove(basic_registry &owner, const Entity entt) { + destruction.publish(entt, owner); storage::destroy(entt); } template - decltype(auto) replace(basic_registry ®istry, const Entity entt, Args &&... args) { - if constexpr(std::is_empty_v) { + decltype(auto) replace(basic_registry &owner, const Entity entt, Args &&... args) { + if constexpr(ENTT_ENABLE_ETO(Component)) { ENTT_ASSERT((storage::has(entt))); - update.publish(entt, registry, Component{}); + update.publish(entt, owner, Component{}); return Component{std::forward(args)...}; } else { Component component{std::forward(args)...}; - update.publish(entt, registry, component); + update.publish(entt, owner, component); return (storage::get(entt) = std::move(component)); } } private: - using reference_type = std::conditional_t, const Component &, Component &>; + using reference_type = std::conditional_t; sigh construction{}; sigh update{}; sigh destruction{}; @@ -7285,61 +7841,68 @@ class basic_registry { struct group_handler; template - struct group_handler, get_t>: sparse_set { - std::tuple *..., pool_type *...> cpools{}; + struct group_handler, get_t> { + const std::tuple *..., pool_type *...> cpools{}; + sparse_set set{}; 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) || ...)) + && (!std::get *>(cpools)->has(entt) && ...) + && !set.has(entt)) { - this->construct(entt); + set.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); + && ((std::is_same_v || !std::get *>(cpools)->has(entt)) && ...) + && !set.has(entt)) + { + set.construct(entt); } } } void discard_if(const Entity entt) { - if(this->has(entt)) { - this->destroy(entt); + if(set.has(entt)) { + set.destroy(entt); } } }; template - struct group_handler, get_t, Owned...>: group_type { - std::tuple *..., pool_type *..., pool_type *...> cpools{}; + struct group_handler, get_t, Owned...> { + const std::tuple *..., pool_type *..., pool_type *...> cpools{}; + std::size_t owned{}; 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) || ...)) + && (!std::get *>(cpools)->has(entt) && ...) + && !(std::get<0>(cpools)->index(entt) < owned)) { - const auto pos = this->owned++; - (std::get *>(cpools)->swap(std::get *>(cpools)->index(entt), pos), ...); + const auto pos = owned++; + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); } } 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)) && ...)) + && ((std::is_same_v || !std::get *>(cpools)->has(entt)) && ...) + && !(std::get<0>(cpools)->index(entt) < owned)) { - const auto pos = this->owned++; - (std::get *>(cpools)->swap(std::get *>(cpools)->index(entt), pos), ...); + const auto pos = owned++; + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); } } } 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), ...); + if(std::get<0>(cpools)->has(entt) && std::get<0>(cpools)->index(entt) < owned) { + const auto pos = --owned; + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entt), ...); } } }; @@ -7353,9 +7916,11 @@ class basic_registry { }; struct group_data { - const std::size_t extent[3]; + std::size_t extent[3]; std::unique_ptr group; - bool(* const is_same)(const component *) ENTT_NOEXCEPT; + bool(* owned)(const component) ENTT_NOEXCEPT; + bool(* get)(const component) ENTT_NOEXCEPT; + bool(* exclude)(const component) ENTT_NOEXCEPT; }; struct ctx_variable { @@ -7366,12 +7931,30 @@ class basic_registry { template static ENTT_ID_TYPE runtime_type() ENTT_NOEXCEPT { if constexpr(is_named_type_v) { - return named_type_traits::value; + return named_type_traits_v; } else { - return Family::template type; + return Family::template type>; } } + auto generate() { + Entity entt; + + if(destroyed == null) { + entt = entities.emplace_back(entity_type(entities.size())); + // traits_type::entity_mask is reserved to allow for null identifiers + ENTT_ASSERT(to_integer(entt) < traits_type::entity_mask); + } else { + const auto curr = to_integer(destroyed); + const auto version = to_integer(entities[curr]) & (traits_type::version_mask << traits_type::entity_shift); + destroyed = entity_type{to_integer(entities[curr]) & traits_type::entity_mask}; + entt = entity_type{curr | version}; + entities[curr] = entt; + } + + return entt; + } + void release(const Entity entity) { // lengthens the implicit list of destroyed entities const auto entt = to_integer(entity) & traits_type::entity_mask; @@ -7382,27 +7965,7 @@ class basic_registry { } 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 pool_type * assure() const { const auto ctype = to_integer(type()); pool_data *pdata = nullptr; @@ -7428,8 +7991,8 @@ class basic_registry { 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); + pdata->remove = [](sparse_set &cpool, basic_registry &owner, const Entity entt) { + static_cast &>(cpool).remove(owner, entt); }; if constexpr(std::is_copy_constructible_v>) { @@ -7449,13 +8012,18 @@ class basic_registry { return static_cast *>(pdata->pool.get()); } + template + pool_type * assure() { + return const_cast *>(std::as_const(*this).template assure()); + } + 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; + using size_type = std::size_t; /*! @brief Default constructor. */ basic_registry() ENTT_NOEXCEPT = default; @@ -7470,8 +8038,7 @@ public: * @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. + * Identifiers 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. @@ -7481,6 +8048,15 @@ public: return component{runtime_type()}; } + /** + * @brief Prepares pools for the given types if required. + * @tparam Component Types of components for which to prepare pools. + */ + template + void prepare() { + (assure(), ...); + } + /** * @brief Returns the number of existing components of the given type. * @tparam Component Type of component of which to return the size. @@ -7488,8 +8064,7 @@ public: */ template size_type size() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->size() : size_type{}; + return assure()->size(); } /** @@ -7505,41 +8080,36 @@ public: * @return Number of entities still in use. */ size_type alive() const ENTT_NOEXCEPT { + auto sz = entities.size(); auto curr = destroyed; - size_type cnt{}; - while(curr != null) { + for(; curr != null; --sz) { curr = entities[to_integer(curr) & traits_type::entity_mask]; - ++cnt; } - return entities.size() - cnt; + return sz; } /** - * @brief Increases the capacity of the pool for the given component. + * @brief Increases the capacity of the registry or of the pools for the + * given components. * - * If the new capacity is greater than the current capacity, new storage is - * allocated, otherwise the method does nothing. + * 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 Type of component for which to reserve storage. + * @tparam Component Types of components for which to reserve storage. * @param cap Desired capacity. */ - template + 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); + if constexpr(sizeof...(Component) == 0) { + entities.reserve(cap); + } else { + (assure()->reserve(cap), ...); + } } /** @@ -7549,8 +8119,7 @@ public: */ template size_type capacity() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->capacity() : size_type{}; + return assure()->capacity(); } /** @@ -7563,32 +8132,33 @@ public: } /** - * @brief Requests the removal of unused capacity for a given component. - * @tparam Component Type of component for which to reclaim unused capacity. + * @brief Requests the removal of unused capacity for the given components. + * @tparam Component Types of components for which to reclaim unused + * capacity. */ - template + template void shrink_to_fit() { - assure()->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. + * @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 + 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(); + if constexpr(sizeof...(Component) == 0) { + return !alive(); + } else { + return (assure()->empty() && ...); + } } /** @@ -7602,17 +8172,15 @@ public: * 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. + * Empty components aren't explicitly instantiated. Therefore, this function + * isn't available for them. A compilation error will occur if invoked. * * @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; + return assure()->raw(); } /*! @copydoc raw */ @@ -7636,8 +8204,7 @@ public: */ template const entity_type * data() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool ? cpool->data() : nullptr; + return assure()->data(); } /** @@ -7711,14 +8278,11 @@ public: */ template auto create() { - entity_type entities[1]{}; - if constexpr(sizeof...(Component) == 0) { - create(std::begin(entities), std::end(entities)); - return entities[0]; + return generate(); } else { - auto it = create(std::begin(entities), std::end(entities)); - return std::tuple(entities[0]))...>{entities[0], *std::get::iterator_type>(it)...}; + const entity_type entt = generate(); + return std::tuple({}))...>{entt, assign(entt)...}; } } @@ -7740,25 +8304,7 @@ public: */ 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; - }); + std::generate(first, last, [this]() { return generate(); }); if constexpr(sizeof...(Component) > 0) { // the reverse iterators guarantee the ordering between entities and components (hint: the pools return begin()) @@ -7786,10 +8332,10 @@ public: * @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]; + entity_type create(entity_type src, const basic_registry &other, exclude_t = {}) { + const auto entt = create(); + stomp(entt, src, other, exclude); + return entt; } /** @@ -7807,6 +8353,7 @@ public: * uses the batch creation under the hood. * * @tparam Component Types of components to copy. + * @tparam It Type of input iterator. * @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. @@ -7814,14 +8361,14 @@ public: * @param other The registry that owns the source entity. */ template - void create(It first, It last, entity_type src, basic_registry &other, exclude_t = {}) { + void create(It first, It last, entity_type src, const 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)), ...); + static_assert(sizeof...(Exclude) == 0); + (assure()->batch(*this, std::make_reverse_iterator(last), std::make_reverse_iterator(first), other.get(src)), ...); } } @@ -7854,7 +8401,7 @@ public: 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)); @@ -7867,8 +8414,8 @@ public: * @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. + * @param first An iterator to the first element of the range to destroy. + * @param last An iterator past the last element of the range to destroy. */ template void destroy(It first, It last) { @@ -7918,7 +8465,7 @@ public: template void remove(const entity_type entity) { ENTT_ASSERT(valid(entity)); - pool()->remove(*this, entity); + assure()->remove(*this, entity); } /** @@ -7936,8 +8483,7 @@ public: 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) && ...); + return (assure()->has(entity) && ...); } /** @@ -7959,9 +8505,9 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (pool()->get(entity), ...); + return (assure()->get(entity), ...); } else { - return std::tuple(entity))...>{get(entity)...}; + return std::tuple({}))...>{get(entity)...}; } } @@ -7971,9 +8517,9 @@ public: ENTT_ASSERT(valid(entity)); if constexpr(sizeof...(Component) == 1) { - return (pool()->get(entity), ...); + return (assure()->get(entity), ...); } else { - return std::tuple(entity))...>{get(entity)...}; + return std::tuple({}))...>{get(entity)...}; } } @@ -8025,10 +8571,9 @@ public: 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), ...); + return (assure()->try_get(entity), ...); } else { - return std::tuple *...>{try_get(entity)...}; + return std::tuple({}))...>{try_get(entity)...}; } } @@ -8036,9 +8581,9 @@ public: 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)), ...); + return (assure()->try_get(entity), ...); } else { - return std::tuple{try_get(entity)...}; + return std::tuple({}))...>{try_get(entity)...}; } } @@ -8065,7 +8610,7 @@ public: template decltype(auto) replace(const entity_type entity, Args &&... args) { ENTT_ASSERT(valid(entity)); - return pool()->replace(*this, entity, std::forward(args)...); + return assure()->replace(*this, entity, std::forward(args)...); } /** @@ -8227,10 +8772,9 @@ public: * 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. + * Pools of components owned by a group cannot be sorted.
+ * An assertion will abort the execution at runtime in debug mode in case + * the pool is owned by a group. * * @tparam Component Type of components to sort. * @tparam Compare Type of comparison function object. @@ -8242,12 +8786,9 @@ public: */ 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)...); - } + auto *cpool = assure(); + ENTT_ASSERT(!cpool->super); + cpool->sort(cpool->begin(), cpool->end(), std::move(compare), std::move(algo), std::forward(args)...); } /** @@ -8278,7 +8819,7 @@ public: * 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.
+ * Pools of components owned by a group cannot be sorted.
* An assertion will abort the execution at runtime in debug mode in case * the pool is owned by a group. * @@ -8287,8 +8828,9 @@ public: */ template void sort() { - ENTT_ASSERT(!owned()); - assure()->respect(*assure()); + auto *cpool = assure(); + ENTT_ASSERT(!cpool->super); + cpool->respect(*assure()); } /** @@ -8460,29 +9002,31 @@ public: * 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 - entt::basic_view view() { - return { assure()... }; + template + entt::basic_view, Component...> view(exclude_t = {}) { + static_assert(sizeof...(Component) > 0); + return { assure()..., assure()... }; } /*! @copydoc view */ - template - entt::basic_view view() const { + template + entt::basic_view, Component...> view(exclude_t = {}) const { static_assert(std::conjunction_v...>); - return const_cast(this)->view(); + return const_cast(this)->view(exclude); } /** - * @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. + * @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 - bool owned() const ENTT_NOEXCEPT { - const auto *cpool = pool(); - return cpool && cpool->group; + template + bool sortable() const ENTT_NOEXCEPT { + return !(assure()->super || ...); } /** @@ -8519,73 +9063,94 @@ public: 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; + [[maybe_unused]] constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); + const auto cpools = std::make_tuple(assure()..., assure()..., assure()...); + const std::size_t extent[3]{sizeof...(Owned), sizeof...(Get), sizeof...(Exclude)}; + handler_type *handler = 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); + if(auto it = std::find_if(groups.cbegin(), groups.cend(), [&extent](const auto &gdata) { + return std::equal(std::begin(extent), std::end(extent), std::begin(gdata.extent)) + && (gdata.owned(type()) && ...) + && (gdata.get(type()) && ...) + && (gdata.exclude(type()) && ...); }); it != groups.cend()) { - curr = static_cast(it->group.get()); + handler = static_cast(it->group.get()); } - if(!curr) { - groups.push_back(group_data{ + if(!handler) { + const void *maybe_valid_if = nullptr; + const void *discard_if = nullptr; + + group_data gdata{ { sizeof...(Owned), sizeof...(Get), sizeof...(Exclude) }, - decltype(group_data::group){new handler_type{}, [](void *gptr) { delete static_cast(gptr); }}, - [](const component *other) ENTT_NOEXCEPT { - const component ctypes[] = { type()..., type()..., type()... }; - return std::equal(std::begin(ctypes), std::end(ctypes), other); - } - }); + decltype(group_data::group){new handler_type{cpools}, [](void *gptr) { delete static_cast(gptr); }}, + [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type()) || ...); }, + [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type()) || ...); }, + [](const component ctype) ENTT_NOEXCEPT { return ((ctype == type()) || ...); } + }; - curr = static_cast(groups.back().group.get()); + if constexpr(sizeof...(Owned) == 0) { + handler = static_cast(groups.emplace_back(std::move(gdata)).group.get()); + } else { + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&extent](const auto &curr) { + const std::size_t diff[3]{ (0u + ... + curr.owned(type())), (0u + ... + curr.get(type())), (0u + ... + curr.exclude(type())) }; + return !diff[0] || ((std::equal(std::begin(diff), std::end(diff), extent) || std::equal(std::begin(diff), std::end(diff), curr.extent))); + })); - ((std::get *>(curr->cpools) = assure()), ...); - ((std::get *>(curr->cpools) = assure()), ...); - ((std::get *>(curr->cpools) = assure()), ...); + const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [&size](const auto &curr) { + const std::size_t diff = (0u + ... + curr.owned(type())); + return !diff || (size > (curr.extent[0] + curr.extent[1] + curr.extent[2])); + }); - ENTT_ASSERT((!std::get *>(curr->cpools)->group && ...)); + const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &curr) { + return (0u + ... + curr.owned(type())); + }); - ((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), ...); + maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); + discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); + handler = static_cast(groups.insert(next, std::move(gdata))->group.get()); + } - (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 *>(cpools)->super = std::max(std::get *>(cpools)->super, size)), ...); - (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), ...); + (std::get *>(cpools)->on_construct().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + (std::get *>(cpools)->on_construct().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + (std::get *>(cpools)->on_destroy().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>(*handler), ...); + + (std::get *>(cpools)->on_destroy().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (std::get *>(cpools)->on_destroy().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); + (std::get *>(cpools)->on_construct().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); const auto *cpool = std::min({ - static_cast *>(std::get *>(curr->cpools))..., - static_cast *>(std::get *>(curr->cpools))... + static_cast *>(std::get *>(cpools))..., + static_cast *>(std::get *>(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) || ...)) + std::for_each(cpool->data(), cpool->data() + cpool->size(), [cpools, handler](const auto entity) { + if((std::get *>(cpools)->has(entity) && ...) + && (std::get *>(cpools)->has(entity) && ...) + && !(std::get *>(cpools)->has(entity) || ...)) { if constexpr(sizeof...(Owned) == 0) { - curr->construct(entity); + handler->set.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(!(std::get<0>(cpools)->index(entity) < handler->owned)) { + const auto pos = handler->owned++; + (std::get *>(cpools)->swap(std::get *>(cpools)->data()[pos], entity), ...); + } } } }); } if constexpr(sizeof...(Owned) == 0) { - return { static_cast *>(curr), std::get *>(curr->cpools)... }; + return { &handler->set, std::get *>(cpools)... }; } else { - return { &curr->owned, std::get *>(curr->cpools)... , std::get *>(curr->cpools)... }; + return { &std::get<0>(cpools)->super, &handler->owned, std::get *>(cpools)... , std::get *>(cpools)... }; } } @@ -8656,10 +9221,9 @@ public: */ 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::vector *> selected(std::distance(first, last)); - std::transform(first, last, set.begin(), [this](const component ctype) { + std::transform(first, last, selected.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; }); @@ -8667,7 +9231,7 @@ public: return it != pools.cend() && it->pool ? it->pool.get() : nullptr; }); - return { std::move(set) }; + return { std::move(selected) }; } /** @@ -8769,9 +9333,9 @@ public: * @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); + void stomp(const entity_type dst, const entity_type src, const basic_registry &other, exclude_t = {}) { + const entity_type entt[1]{dst}; + stomp(std::begin(entt), std::end(entt), src, other, exclude); } /** @@ -8780,6 +9344,7 @@ public: * @sa stomp * * @tparam Component Types of components to copy. + * @tparam It Type of input iterator. * @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. @@ -8787,9 +9352,8 @@ public: * @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 = {}) { + void stomp(It first, It last, const entity_type src, const 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]; @@ -8936,8 +9500,8 @@ public: */ template Type & ctx_or_set(Args &&... args) { - auto *type = try_ctx(); - return type ? *type : set(std::forward(args)...); + auto *value = try_ctx(); + return value ? *value : set(std::forward(args)...); } /** @@ -8987,8 +9551,8 @@ public: } private: - std::size_t skip_family_pools{}; - std::vector pools{}; + mutable std::size_t skip_family_pools{}; + mutable std::vector pools{}; std::vector groups{}; std::vector vars{}; std::vector entities{}; @@ -9241,12 +9805,13 @@ struct as_view { /** * @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 entt::basic_view() const { - return reg.template view(); + template + operator entt::basic_view() const { + return reg.template view(Exclude{}); } private: @@ -9410,7 +9975,7 @@ struct basic_collector<> { */ template static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { - return basic_collector, type_list<>>, type_list, type_list>>{}; + return basic_collector, type_list<>, type_list, AllOf...>>{}; } /** @@ -9420,18 +9985,23 @@ struct basic_collector<> { */ template static constexpr auto replace() ENTT_NOEXCEPT { - return basic_collector, type_list<>>, AnyOf>>{}; + 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. + * @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...> { +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. @@ -9440,8 +10010,7 @@ struct basic_collector, type_list static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { - using first = matcher, type_list>, Rule...>; - return basic_collector, type_list<>>, type_list, type_list>>{}; + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; } /** @@ -9451,8 +10020,7 @@ struct basic_collector, type_list static constexpr auto replace() ENTT_NOEXCEPT { - using first = matcher, type_list>, Rule...>; - return basic_collector, type_list<>>, AnyOf>>{}; + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; } /** @@ -9463,7 +10031,8 @@ struct basic_collector, type_list static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { - return basic_collector, type_list>, Rule...>, Other...>{}; + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; } }; @@ -9529,7 +10098,7 @@ class basic_observer { struct matcher_handler; template - struct matcher_handler, type_list>, AnyOf>> { + 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) || ...)) { @@ -9562,7 +10131,7 @@ class basic_observer { }; template - struct matcher_handler, type_list>, type_list, type_list>> { + struct matcher_handler, type_list, type_list, AllOf...>> { template static void maybe_valid_if(basic_observer &obs, const Entity entt, const basic_registry ®) { if(reg.template has(entt) && !(reg.template has(entt) || ...) @@ -9641,7 +10210,7 @@ public: release{}, view{} { - connect(reg, std::make_index_sequence{}); + connect(reg, std::index_sequence_for{}); } /*! @brief Default destructor. */ @@ -9667,7 +10236,7 @@ public: template void connect(basic_registry ®, basic_collector) { disconnect(); - connect(reg, std::make_index_sequence{}); + connect(reg, std::index_sequence_for{}); target = ® view.reset(); } @@ -9844,6 +10413,15 @@ private: #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -10009,3132 +10587,13 @@ private: #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 "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 +#ifndef ENTT_DISABLE_ETO #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; - - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -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; - } 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 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. - * @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 - - - -namespace entt { - - -/** - * @cond TURN_OFF_DOXYGEN - * Internal details not to be documented. - */ - - -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; - } - } - -public: - /*! @brief Default constructor. */ - meta_factory() ENTT_NOEXCEPT = default; - - /** - * @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 +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO #ifndef ENTT_ID_TYPE @@ -13158,1610 +10617,70 @@ resolve(Op op) ENTT_NOEXCEPT { #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 - - -#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 // #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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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 "../core/hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP @@ -14797,6 +10716,15 @@ public: #endif // ENTT_NO_ATOMIC +#ifndef ENTT_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + #ifndef ENTT_ID_TYPE #include #define ENTT_ID_TYPE std::uint32_t @@ -15071,6 +10999,32 @@ constexpr entt::hashed_wstring operator"" ENTT_HWS_SUFFIX(const wchar_t *str, st 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 TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +constexpr choice_t choice{}; + + /*! @brief A class to use to push around lists of types, nothing more. */ template struct type_list {}; @@ -15181,6 +11135,28 @@ template using type_list_unique_t = typename type_list_unique::type; +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + /*! @brief Traits class used mainly to push things across boundaries. */ template struct named_type_traits; @@ -15205,11 +11181,11 @@ 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. + * @brief Helper variable template. + * @tparam Type Potentially named type. */ -template> -struct is_named_type: std::false_type {}; +template +constexpr auto named_type_traits_v = named_type_traits::value; /** @@ -15217,6 +11193,11 @@ struct is_named_type: std::false_type {}; * name. In all other cases, `value` is false. * @tparam Type Potentially named type. */ +template> +struct is_named_type: std::false_type {}; + + +/*! @copydoc is_named_type */ template struct is_named_type>>>: std::true_type {}; @@ -15239,7 +11220,8 @@ constexpr auto is_named_type_v = is_named_type::value; enum class clazz: type {};\ constexpr auto to_integer(const clazz id) ENTT_NOEXCEPT {\ return std::underlying_type_t(id);\ - } + }\ + static_assert(true) } @@ -15277,9 +11259,5231 @@ constexpr auto is_named_type_v = is_named_type::value; struct entt::named_type_traits\ : std::integral_constant>>>{#type}>\ {\ - static_assert(std::is_same_v, type>);\ + static_assert(std::is_same_v, type>);\ + static_assert(std::is_object_v);\ + } + + +/** + * @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 "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 "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + + +// #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 + 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. + */ +template +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. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +constexpr auto overload(Type *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(Type...) -> 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 // ENTT_CORE_UTILITY_HPP + + + +namespace entt { + + +class meta_any; +class meta_handle; +class meta_type; + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +namespace internal { + + +struct meta_type_node; + + +struct meta_prop_node { + meta_prop_node * next; + meta_any(* const key)(); + meta_any(* const value)(); +}; + + +struct meta_base_node { + meta_type_node * const parent; + meta_base_node * next; + meta_type_node *(* const type)() ENTT_NOEXCEPT; + void *(* const cast)(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 size; + meta_type_node *(* const arg)(size_type) ENTT_NOEXCEPT; + meta_any(* const invoke)(meta_any * const); +}; + + +struct meta_dtor_node { + meta_type_node * const parent; + bool(* const invoke)(meta_handle); +}; + + +struct meta_data_node { + 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); +}; + + +struct meta_func_node { + using size_type = std::size_t; + 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 *); +}; + + +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_pointer; + const bool is_member_object_pointer; + const bool is_member_function_pointer; + const size_type extent; + bool(* const compare)(const void *, const void *); + meta_type_node *(* const remove_pointer)() ENTT_NOEXCEPT; + meta_type_node *(* const remove_extent)() 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 +void iterate(Op op, Node *curr) ENTT_NOEXCEPT { + while(curr) { + op(Type{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, 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 +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 +struct meta_node; + + +template<> +struct meta_node<> { + inline static meta_type_node *local = nullptr; + inline static meta_type_node **global = &local; +}; + + +template +struct meta_node { + static_assert(std::is_same_v>>); + + static void reset() ENTT_NOEXCEPT { + auto * const node = resolve(); + auto **it = meta_node<>::global; + + while(*it && *it != node) { + it = &(*it)->next; + } + + if(*it) { + *it = (*it)->next; + } + + const auto unregister_all = y_combinator{ + [](auto &&self, auto **curr, auto... member) { + while(*curr) { + auto *prev = *curr; + (self(&(prev->*member)), ...); + *curr = prev->next; + prev->next = nullptr; + } + } + }; + + unregister_all(&node->prop); + unregister_all(&node->base); + unregister_all(&node->conv); + unregister_all(&node->ctor, &internal::meta_ctor_node::prop); + unregister_all(&node->data, &internal::meta_data_node::prop); + unregister_all(&node->func, &internal::meta_func_node::prop); + + node->identifier = {}; + node->dtor = nullptr; + node->next = nullptr; + } + + static meta_type_node * resolve() ENTT_NOEXCEPT { + 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_pointer_v && std::is_function_v>, + std::is_member_object_pointer_v, + std::is_member_function_pointer_v, + std::extent_v, + &compare, // workaround for an issue with VS2017 + []() ENTT_NOEXCEPT -> meta_type_node * { + return meta_node>>::resolve(); + }, + []() ENTT_NOEXCEPT -> meta_type_node * { + return meta_node>>::resolve(); + } + }; + + if constexpr(is_named_type_v) { + auto *candidate = internal::find_if([](auto *curr) { + return curr->identifier == named_type_traits_v; + }, *meta_node<>::global); + + return candidate ? candidate : &node; + } else { + return &node; + } + } +}; + + +template +struct meta_info: meta_node>...> {}; + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +/** + * @brief Opaque container for 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. + * + * @warning + * Only copy constructible types are suitable for use with this class. A static + * assertion will abort the compilation when the type provided isn't copy + * constructible. + */ +class meta_any { + /*! @brief A meta handle is allowed to _inherit_ from a meta any. */ + friend class meta_handle; + + using storage_type = std::aligned_storage_t; + 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 + static Type * release(Type *instance) { + const auto * const node = internal::meta_info::resolve(); + [[maybe_unused]] const bool destroyed = (!node->dtor || node->dtor->invoke(*instance)); + ENTT_ASSERT(destroyed); + return instance; + } + + 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) { + delete release(static_cast(instance)); + } + + 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 * const 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) { + release(static_cast(instance))->~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 * const instance = new (&to) Type{std::move(*static_cast(from))}; + destroy_fn(from); + return instance; + } + }; + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + instance{nullptr}, + node{nullptr}, + destroy_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>>; + static_assert(std::is_copy_constructible_v); + + instance = traits_type::instance(storage, std::forward(args)...); + destroy_fn = &traits_type::destroy; + copy_fn = &traits_type::copy; + steal_fn = &traits_type::steal; + } + } + + /** + * @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(std::reference_wrapper type) + : meta_any{} + { + node = internal::meta_info::resolve(); + instance = &type.get(); + } + + /** + * @brief Constructs a meta any from a meta handle object. + * @param handle A reference to an object to use to initialize the meta any. + */ + inline meta_any(meta_handle handle) ENTT_NOEXCEPT; + + /** + * @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; + 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 { + const auto * const type = internal::meta_info::resolve(); + void *ret = nullptr; + + if(node == type) { + ret = instance; + } else { + const auto *base = internal::find_if<&internal::meta_type_node::base>([type](auto *candidate) { + return candidate->type() == type; + }, node); + + ret = base ? base->cast(instance) : nullptr; + } + + return static_cast(ret); + } + + /*! @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 * const 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 * const type = internal::meta_info::resolve(); node == type) { + any = *static_cast(instance); + } else { + const auto * const 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 && (!node || node->compare(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 * const 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.copy_fn, rhs.copy_fn); + std::swap(lhs.steal_fn, rhs.steal_fn); + } + +private: + storage_type storage; + void *instance; + const internal::meta_type_node *node; + destroy_fn_type *destroy_fn; + copy_fn_type *copy_fn; + steal_fn_type *steal_fn; +}; + + +/** + * @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. Users are + * responsible for ensuring that the target object remains alive for the entire + * interval of use of the handle. + */ +class meta_handle { + /*! @brief A meta any is allowed to _inherit_ from a meta handle. */ + friend class meta_any; + +public: + /*! @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} + {} + + /*! @copydoc meta_any::type */ + inline meta_type type() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::data */ + const void * data() const ENTT_NOEXCEPT { + return instance; + } + + /*! @copydoc data */ + void * 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 Opaque container for meta properties of any type. */ +struct meta_prop { + /** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const internal::meta_prop_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @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 Opaque container for meta base classes. */ +struct meta_base { + /*! @copydoc meta_prop::meta_prop */ + meta_base(const internal::meta_base_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the meta type to which a meta object belongs. + * @return The meta type to which the meta object belongs. + */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + 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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_base &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_base_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_base &lhs, const meta_base &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta conversion functions. */ +struct meta_conv { + /*! @copydoc meta_prop::meta_prop */ + meta_conv(const internal::meta_conv_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + inline meta_type parent() const ENTT_NOEXCEPT; + + /*! @copydoc meta_any::type */ + 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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_conv &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_conv_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_conv &lhs, const meta_conv &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta constructors. */ +struct meta_ctor { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_ctor_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_ctor(const internal::meta_ctor_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + 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(std::move(op), 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. + */ + meta_prop prop(meta_any key) const ENTT_NOEXCEPT { + return internal::find_if([key = std::move(key)](auto *candidate) { + return candidate->key() == key; + }, node->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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_ctor &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_ctor_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_ctor &lhs, const meta_ctor &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta destructors. */ +struct meta_dtor { + /*! @copydoc meta_prop::meta_prop */ + meta_dtor(const internal::meta_dtor_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_base::parent */ + 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 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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_dtor &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_dtor_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_dtor &lhs, const meta_dtor &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta data. */ +struct meta_data { + /*! @copydoc meta_prop::meta_prop */ + meta_data(const internal::meta_data_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::identifier */ + ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT { + return node->identifier; + } + + /*! @copydoc meta_base::parent */ + 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; + } + + /*! @copydoc meta_any::type */ + 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. + * @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(std::move(op), 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. + */ + meta_prop prop(meta_any key) const ENTT_NOEXCEPT { + return internal::find_if([key = std::move(key)](auto *candidate) { + return candidate->key() == key; + }, node->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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_data &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_data_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_data &lhs, const meta_data &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_func(const internal::meta_func_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /*! @copydoc meta_type::identifier */ + ENTT_ID_TYPE identifier() const ENTT_NOEXCEPT { + return node->identifier; + } + + /*! @copydoc meta_base::parent */ + 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 { + // makes aliasing on the values and passes forward references if any + std::array arguments{{meta_handle{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(std::move(op), 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. + */ + meta_prop prop(meta_any key) const ENTT_NOEXCEPT { + return internal::find_if([key = std::move(key)](auto *candidate) { + return candidate->key() == key; + }, node->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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_func &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_func_node *node; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_func &lhs, const meta_func &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +/*! @brief Opaque container for meta types. */ +class meta_type { + template + auto ctor(std::index_sequence) const ENTT_NOEXCEPT { + return internal::find_if([](auto *candidate) { + return candidate->size == sizeof...(Args) && ([](auto *from, auto *to) { + return (from == to) || internal::find_if<&internal::meta_type_node::base>([to](auto *curr) { return curr->type() == to; }, from) + || internal::find_if<&internal::meta_type_node::conv>([to](auto *curr) { return curr->type() == to; }, from); + }(internal::meta_info::resolve(), candidate->arg(Indexes)) && ...); + }, node->ctor); + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_type(const internal::meta_type_node *curr = nullptr) ENTT_NOEXCEPT + : node{curr} + {} + + /** + * @brief Returns the identifier assigned to a given meta object. + * @return The identifier assigned to the meta object. + */ + 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 pointer + * or not. + * @return True if the underlying type is a function pointer, false + * otherwise. + */ + bool is_function_pointer() const ENTT_NOEXCEPT { + return node->is_function_pointer; + } + + /** + * @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 Provides the meta type for which the array is defined. + * @return The meta type for which the array is defined or this meta type + * if it doesn't refer to an array type. + */ + meta_type remove_extent() const ENTT_NOEXCEPT { + return node->remove_extent(); + } + + /** + * @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, meta_base>(std::move(op), 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 { + return internal::find_if<&internal::meta_type_node::base>([identifier](auto *candidate) { + return candidate->type()->identifier == identifier; + }, node); + } + + /** + * @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, meta_conv>(std::move(op), 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 { + return internal::find_if<&internal::meta_type_node::conv>([type = internal::meta_info::resolve()](auto *candidate) { + return candidate->type() == type; + }, node); + } + + /** + * @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(std::move(op), 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 { + return ctor(std::index_sequence_for{}); + } + + /** + * @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; + } + + /** + * @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, meta_data>(std::move(op), 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 { + return internal::find_if<&internal::meta_type_node::data>([identifier](auto *candidate) { + return candidate->identifier == identifier; + }, node); + } + + /** + * @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, meta_func>(std::move(op), 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 { + return internal::find_if<&internal::meta_type_node::func>([identifier](auto *candidate) { + return candidate->identifier == identifier; + }, node); + } + + /** + * @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) && (!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, meta_prop>(std::move(op), 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. + * + * @param key The key to use to search for a property. + * @return The property associated with the given key, if any. + */ + meta_prop prop(meta_any key) const ENTT_NOEXCEPT { + return internal::find_if<&internal::meta_type_node::prop>([key = std::move(key)](auto *candidate) { + return candidate->key() == key; + }, node); + } + + /** + * @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; + } + + /*! @copydoc meta_prop::operator== */ + bool operator==(const meta_type &other) const ENTT_NOEXCEPT { + return node == other.node; + } + +private: + const internal::meta_type_node *node; +}; + + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to the given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_info<>::global = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_info<>::local}; +}; + + +/*! @copydoc operator!=(const meta_prop &, const meta_prop &) */ +inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + + +inline meta_any::meta_any(meta_handle handle) ENTT_NOEXCEPT + : meta_any{} +{ + node = handle.node; + instance = handle.instance; +} + + +inline meta_type meta_any::type() const ENTT_NOEXCEPT { + return node; +} + + +inline meta_type meta_handle::type() const ENTT_NOEXCEPT { + return node; +} + + +inline meta_type meta_base::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_base::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_conv::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_conv::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_ctor::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_ctor::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +inline meta_type meta_dtor::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_data::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_data::type() const ENTT_NOEXCEPT { + return node->type(); +} + + +inline meta_type meta_func::parent() const ENTT_NOEXCEPT { + return node->parent; +} + + +inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret(); +} + + +inline meta_type meta_func::arg(size_type index) const ENTT_NOEXCEPT { + return index < size() ? node->arg(index) : nullptr; +} + + +} + + +#endif // ENTT_META_META_HPP + + + +namespace entt { + + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + + +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 std::index_sequence_for index_sequence{}; + 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...)); + + +constexpr void to_meta_function_helper(...); + + +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 * const clazz = meta_any{handle}.try_cast(); + auto * const direct = value.try_cast(); + + 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 * const clazz = meta_any{handle}.try_cast(); + + if constexpr(std::is_array_v) { + using underlying_type = std::remove_extent_t; + auto * const direct = value.try_cast(); + auto * const idx = index.try_cast(); + + if(clazz && idx && (direct || value.convert())) { + std::invoke(Data, clazz)[*idx] = direct ? *direct : value.cast(); + accepted = true; + } + } else { + auto * const 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 * const direct = value.try_cast(); + auto * const idx = index.try_cast(); + + if(idx && (direct || value.convert())) { + (*Data)[*idx] = (direct ? *direct : value.cast()); + accepted = true; + } + } else { + auto * const 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{std::ref(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 * const clazz = meta_any{handle}.try_cast(); + 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 * const clazz = meta_any{handle}.try_cast(); + + if constexpr(std::is_array_v) { + auto * const 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 * const 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 *... params) { + if constexpr(std::is_void_v || std::is_same_v) { + std::invoke(Candidate, *params...); + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::ref(std::invoke(Candidate, *params...))}; + } else { + static_assert(std::is_same_v); + return meta_any{std::invoke(Candidate, *params...)}; + } + }; + + [[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 * const clazz = meta_any{handle}.try_cast(); + return (clazz && (std::get(direct) && ...)) ? dispatch(clazz, std::get(direct)...) : meta_any{}; + } +} + + +} + + +/** + * Internal details not to be documented. + * @endcond TURN_OFF_DOXYGEN + */ + + +template +class extended_meta_factory; + + +/** + * @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 { + template + bool duplicate(const Node *candidate, const Node *node) ENTT_NOEXCEPT { + return node && (node == candidate || duplicate(candidate, node->next)); + } + + template + bool duplicate(const ENTT_ID_TYPE identifier, const Node *node) ENTT_NOEXCEPT { + return node && (node->identifier == identifier || duplicate(identifier, node->next)); + } + + auto record(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { + auto * const node = internal::meta_info::resolve(); + + ENTT_ASSERT(!duplicate(identifier, *internal::meta_info<>::global)); + ENTT_ASSERT(!duplicate(node, *internal::meta_info<>::global)); + node->identifier = identifier; + node->next = *internal::meta_info<>::global; + *internal::meta_info<>::global = node; + + return extended_meta_factory{&node->prop}; + } + +public: + /** + * @brief Extends a meta type by assigning it an identifier. + * + * This function is intended only for unnamed types. + * + * @param identifier Unique identifier. + * @return An extended meta factory for the parent type. + */ + auto type(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { + static_assert(!is_named_type_v); + return record(identifier); + } + + /** + * @brief Extends a meta type by assigning it an identifier. + * + * This function is intended only for named types + * + * @return An extended meta factory for the parent type. + */ + auto type() ENTT_NOEXCEPT { + static_assert(is_named_type_v); + return record(named_type_traits_t::value); + } + + /** + * @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); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_base_node node{ + type, + nullptr, + &internal::meta_info::resolve, + [](void *instance) ENTT_NOEXCEPT -> void * { + return static_cast(static_cast(instance)); + } + }; + + ENTT_ASSERT(!duplicate(&node, type->base)); + node.next = type->base; + type->base = &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); + 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)); + } + }; + + ENTT_ASSERT(!duplicate(&node, type->conv)); + node.next = type->conv; + type->conv = &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 + auto 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 std::invoke(Candidate, *static_cast(instance)); + } + }; + + ENTT_ASSERT(!duplicate(&node, type->conv)); + node.next = type->conv; + type->conv = &node; + + return meta_factory{}; + } + + /** + * @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). + * @return An extended meta factory for the parent type. + */ + template + auto ctor() 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{ + type, + nullptr, + nullptr, + helper_type::index_sequence.size(), + &helper_type::arg, + [](meta_any * const any) { + return internal::invoke({}, any, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!duplicate(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return extended_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 helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_ctor_node node{ + type, + nullptr, + nullptr, + helper_type::index_sequence.size(), + &helper_type::arg, + [](meta_any * const any) { + return internal::construct>...>(any, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!duplicate(&node, type->ctor)); + node.next = type->ctor; + type->ctor = &node; + + return extended_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); + auto * const type = internal::meta_info::resolve(); + + static internal::meta_dtor_node node{ + type, + [](meta_handle handle) { + const auto valid = (handle.type() == internal::meta_info::resolve()); + + if(valid) { + std::invoke(Func, *meta_any{handle}.try_cast()); + } + + return valid; + } + }; + + ENTT_ASSERT(!type->dtor); + type->dtor = &node; + + 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 identifier Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const ENTT_ID_TYPE identifier) 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{ + {}, + 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; } + }; + + 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{ + {}, + type, + nullptr, + nullptr, + std::is_const_v, + !std::is_member_object_pointer_v, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, + &internal::getter + }; + + curr = &node; + } else { + static_assert(std::is_pointer_v>); + using data_type = std::remove_pointer_t>; + + static internal::meta_data_node node{ + {}, + type, + nullptr, + nullptr, + std::is_const_v, + !std::is_member_object_pointer_v, + &internal::meta_info::resolve, + &internal::setter, Type, Data>, + &internal::getter + }; + + curr = &node; + } + + ENTT_ASSERT(!duplicate(identifier, type->data)); + ENTT_ASSERT(!duplicate(curr, type->data)); + curr->identifier = identifier; + curr->next = type->data; + type->data = curr; + + return extended_meta_factory>{&curr->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 identifier Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { + 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{ + {}, + type, + nullptr, + nullptr, + false, + false, + &internal::meta_info::resolve, + &internal::setter, + &internal::getter + }; + + ENTT_ASSERT(!duplicate(identifier, type->data)); + ENTT_ASSERT(!duplicate(&node, type->data)); + node.identifier = identifier; + node.next = type->data; + type->data = &node; + + return extended_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 identifier Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto func(const ENTT_ID_TYPE identifier) ENTT_NOEXCEPT { + using helper_type = internal::meta_function_helper_t; + auto * const type = internal::meta_info::resolve(); + + static internal::meta_func_node node{ + {}, + type, + nullptr, + nullptr, + helper_type::index_sequence.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, helper_type::index_sequence); + } + }; + + ENTT_ASSERT(!duplicate(identifier, type->func)); + ENTT_ASSERT(!duplicate(&node, type->func)); + node.identifier = identifier; + node.next = type->func; + type->func = &node; + + return extended_meta_factory>{&node.prop}; + } + + /** + * @brief Resets a meta type and all its parts. + * + * This function resets 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 reset but the link between the two types is removed. + */ + void reset() ENTT_NOEXCEPT { + internal::meta_info::reset(); + } +}; + + +/** + * @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 +class extended_meta_factory: public meta_factory { + bool duplicate(const meta_any &key, const internal::meta_prop_node *node) ENTT_NOEXCEPT { + return node && (node->key() == key || duplicate(key, node->next)); + } + + 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, Value &&... value) { + static auto property{std::make_tuple(std::forward(key), std::forward(value)...)}; + + static internal::meta_prop_node node{ + nullptr, + []() -> meta_any { + return std::get<0>(property); + }, + []() -> meta_any { + if constexpr(sizeof...(Value) == 0) { + return {}; + } else { + return std::get<1>(property); + } + } + }; + + ENTT_ASSERT(!duplicate(node.key(), *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. + */ + extended_meta_factory(entt::internal::meta_prop_node **target) + : 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 extended_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 extended_meta_factory{curr}; + } + +private: + entt::internal::meta_prop_node **curr{nullptr}; +}; + + +/** + * @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 +inline meta_factory meta() ENTT_NOEXCEPT { + return meta_factory{}; +} + + +/** + * @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(); +} + + +/** + * @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 { + return internal::find_if([identifier](auto *node) { + return node->identifier == identifier; + }, *internal::meta_info<>::global); +} + + +/** + * @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(std::move(op), *internal::meta_info<>::global); +} + + +} + + +#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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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({}); + } + } + } + + /** + * @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 = std::size_t; + + /*! @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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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 + + +namespace entt { + + +/*! @struct cache */ +template +struct cache; + +/*! @class handle */ +template +class handle; + +/*! @class loader */ +template +class 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 handle { + /*! @brief Resource handles are friends of their caches. */ + friend struct cache; + + handle(std::shared_ptr res) ENTT_NOEXCEPT + : resource{std::move(res)} + {} + +public: + /*! @brief Default constructor. */ + 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::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 loader { + /*! @brief Resource loaders are friends of their caches. */ + friend struct 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 +struct cache { + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of resources managed by a cache. */ + using resource_type = Resource; + /*! @brief Unique identifier type for resources. */ + using id_type = ENTT_ID_TYPE; + + /*! @brief Default constructor. */ + cache() = default; + + /*! @brief Default move constructor. */ + cache(cache &&) = default; + + /*! @brief Default move assignment operator. @return This cache. */ + cache & operator=(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 + entt::handle load(const id_type id, Args &&... args) { + static_assert(std::is_base_of_v, Loader>); + entt::handle resource{}; + + if(auto it = resources.find(id); it == resources.cend()) { + if(auto instance = Loader{}.get(std::forward(args)...); instance) { + resources[id] = instance; + resource = std::move(instance); + } + } else { + resource = it->second; + } + + return resource; + } + + /** + * @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 + entt::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 + entt::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 handle + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + entt::handle handle(const id_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 id_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 id_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 id_type); + * void(handle); + * void(const id_type, 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(entt::handle{ curr->second }); + } else { + func(curr->first, entt::handle{ curr->second }); + } + } + } + +private: + std::unordered_map> 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 + + +#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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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...), const Payload *) -> Ret(*)(Args...); + + +template>> +auto to_function_pointer(Ret(*)(Type *, Args...), const 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 +using to_function_pointer_t = decltype(internal::to_function_pointer(std::declval()...)); + + +template +constexpr auto index_sequence_for(Ret(*)(Args...)) { + return std::index_sequence_for{}; +} + + +} + + +/** + * 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 = static_cast(const_cast, const void *, void *>>(payload)); + // Ret(...) makes void(...) eat the return values to avoid errors + return Ret(std::invoke(Candidate, *curr, 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 = static_cast(const_cast, const void *, void *>>(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 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 pointer 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 { + connect(internal::index_sequence_for(internal::to_function_pointer_t{})); + } + + /** + * @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 { + connect(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t{})); + } + + /** + * @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 pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) ENTT_NOEXCEPT { + connect(value_or_instance, internal::index_sequence_for(internal::to_function_pointer_t{})); + } + + /** + * @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. + * + * @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 &) 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. + * + * @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 *) 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 +#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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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{}; + +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 = identifier++; +}; + + +} + + +#endif // ENTT_CORE_FAMILY_HPP + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + + +#include +#include +// #include "../config/config.h" + +// #include "../core/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_DISABLE_ETO +#include +#define ENTT_ENABLE_ETO(Type) std::is_empty_v +#else // ENTT_DISABLE_ETO +// sfinae-friendly definition +#define ENTT_ENABLE_ETO(Type) (false && std::is_empty_v) +#endif // ENTT_DISABLE_ETO + + +#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 +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; +}; + + +/** + * @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 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 TURN_OFF_DOXYGEN */ +{}; + + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +constexpr choice_t choice{}; + + +/*! @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 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; + + +/*! @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 given type is + * equality comparable, false otherwise. + * @tparam Type Potentially equality comparable type. + */ +template> +struct is_equality_comparable: std::false_type {}; + + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable() == std::declval())>>: std::true_type {}; + + +/** + * @brief Helper variable template. + * @tparam Type Potentially equality comparable type. + */ +template +constexpr auto is_equality_comparable_v = is_equality_comparable::value; + + +/*! @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 Helper variable template. + * @tparam Type Potentially named type. + */ +template +constexpr auto named_type_traits_v = named_type_traits::value; + + +/** + * @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::false_type {}; + + +/*! @copydoc is_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);\ + }\ + static_assert(true) + + +} + + +/** + * @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>);\ + static_assert(std::is_object_v);\ + } + /** * @brief Defines a named type (to use for structs). @@ -15342,9 +16546,10 @@ constexpr auto is_named_type_v = is_named_type::value; #define ENTT_SIGNAL_SIGH_HPP -#include -#include #include +#include +#include +#include #include #include // #include "../config/config.h" @@ -15356,10 +16561,6 @@ constexpr auto is_named_type_v = is_named_type::value; #define ENTT_SIGNAL_FWD_HPP -// #include "../config/config.h" - - - namespace entt { @@ -15432,7 +16633,7 @@ class sigh { 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; @@ -15467,9 +16668,9 @@ public: * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { - for(auto pos = calls.size(); pos; --pos) { - calls[pos-1](args...); - } + std::for_each(calls.cbegin(), calls.cend(), [&args...](auto &&call) { + call(args...); + }); } /** @@ -15488,22 +16689,20 @@ public: */ 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...)); } } } @@ -15598,6 +16797,9 @@ private: * when it goes out of scope. */ struct scoped_connection: private connection { + using connection::operator bool; + using connection::release; + /*! @brief Default constructor. */ scoped_connection() = default; @@ -15651,9 +16853,6 @@ struct scoped_connection: private connection { static_cast(*this) = std::move(other); return *this; } - - using connection::operator bool; - using connection::release; }; @@ -15674,9 +16873,10 @@ struct scoped_connection: private connection { 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); } @@ -15691,7 +16891,8 @@ public: * @param ref A valid reference to a signal object. */ sink(sigh &ref) ENTT_NOEXCEPT - : signal{&ref} + : offset{}, + signal{&ref} {} /** @@ -15702,6 +16903,111 @@ public: return signal->calls.empty(); } + /** + * @brief Returns a sink that connects before a given function. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + 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 given member function or + * free function with payload. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + * @return A properly initialized sink object. + */ + template + 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 member function or + * free function with payload. + * @tparam Candidate Member or free function to look for. + * @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 + 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 + 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 + 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. + */ + sink before() { + sink other{*this}; + other.offset = signal->calls.size(); + return other; + } + /** * @brief Connects a free function to a signal. * @@ -15714,9 +17020,13 @@ public: 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>(); - signal->calls.emplace_back(delegate{connect_arg}); return { std::move(conn), signal }; } @@ -15740,9 +17050,43 @@ public: 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<&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 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 pointer 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 }; } @@ -15753,9 +17097,9 @@ public: template void disconnect() { auto &calls = signal->calls; - delegate delegate{}; - delegate.template connect(); - calls.erase(std::remove(calls.begin(), calls.end(), delegate), calls.end()); + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** @@ -15768,23 +17112,51 @@ public: 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()); + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), 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 pointer 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 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. + * @param value_or_instance A valid object 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()); + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @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 pointer 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. */ @@ -15793,6 +17165,7 @@ public: } private: + difference_type offset; signal_type *signal; }; @@ -15842,6 +17215,7 @@ class dispatcher { struct base_wrapper { virtual ~base_wrapper() = default; virtual void publish() = 0; + virtual void clear() = 0; }; template @@ -15850,12 +17224,17 @@ class dispatcher { using sink_type = typename signal_type::sink_type; void publish() override { - for(const auto &event: events[current]) { - signal.publish(event); + const auto length = events.size(); + + for(std::size_t pos{}; pos < length; ++pos) { + signal.publish(events[pos]); } - events[current++].clear(); - current %= std::extent::value; + events.erase(events.cbegin(), events.cbegin()+length); + } + + void clear() override { + events.clear(); } sink_type sink() ENTT_NOEXCEPT { @@ -15869,13 +17248,12 @@ class dispatcher { template void enqueue(Args &&... args) { - events[current].emplace_back(std::forward(args)...); + events.emplace_back(std::forward(args)...); } private: signal_type signal{}; - std::vector events[2]; - int current{}; + std::vector events; }; struct wrapper_data { @@ -15886,9 +17264,9 @@ class dispatcher { template static auto type() ENTT_NOEXCEPT { if constexpr(is_named_type_v) { - return named_type_traits::value; + return named_type_traits_v; } else { - return event_family::type; + return event_family::type>; } } @@ -16010,6 +17388,27 @@ public: assure>().enqueue(std::forward(event)); } + /** + * @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 discard() { + if constexpr(sizeof...(Event) == 0) { + std::for_each(wrappers.begin(), wrappers.end(), [](auto &&wdata) { + if(wdata.wrapper) { + wdata.wrapper->clear(); + } + }); + } else { + (assure>().clear(), ...); + } + } + /** * @brief Delivers all the pending events of the given type. * @@ -16033,9 +17432,7 @@ public: */ void update() const { for(auto pos = wrappers.size(); pos; --pos) { - auto &wdata = wrappers[pos-1]; - - if(wdata.wrapper) { + if(auto &wdata = wrappers[pos-1]; wdata.wrapper) { wdata.wrapper->publish(); } } @@ -16180,9 +17577,9 @@ class emitter { template static auto type() ENTT_NOEXCEPT { if constexpr(is_named_type_v) { - return named_type_traits::value; + return named_type_traits_v; } else { - return handler_family::type; + return handler_family::type>; } }