// IWYU pragma: begin_exports // #include "config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "config/macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif // #include "config/version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif // #include "container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "container/dense_set.hpp" #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" // #include "../core/memory.hpp" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class dense_set_iterator final { template friend class dense_set_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; constexpr dense_set_iterator() noexcept : it{} {} constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_set_local_iterator final { template friend class dense_set_local_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for unique objects of a given type. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on its hash. Elements with the same hash code * appear in the same bucket. * * @tparam Type Value type of the associative container. * @tparam Hash Type of function to use to hash the values. * @tparam KeyEqual Type of function to use to compare the values for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_set { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = std::pair; using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&value) { const auto index = value_to_bucket(value); if(auto it = constrained_find(value, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Type; /*! @brief Value type of the container. */ using value_type = Type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the elements. */ using hasher = Hash; /*! @brief Type of function to use to compare the elements for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Random access iterator type. */ using iterator = internal::dense_set_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::dense_set_iterator; /*! @brief Forward iterator type. */ using local_iterator = internal::dense_set_local_iterator; /*! @brief Constant forward iterator type. */ using const_local_iterator = internal::dense_set_local_iterator; /*! @brief Default constructor. */ dense_set() : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_set(const allocator_type &allocator) : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const allocator_type &allocator) : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_set(const dense_set &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_set(const dense_set &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_set(dense_set &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_set &operator=(const dense_set &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if it does not exist. * @param value An element to insert into the container. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value)); } /** * @brief Inserts elements into the container, if they do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Constructs an element in-place, if it does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace(Args &&...args) { if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); const auto index = value_to_bucket(node.second); if(auto it = constrained_find(node.second, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.first, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(*pos); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].second); } return (begin() + dist); } /** * @brief Removes the element associated with a given value. * @param value Value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const value_type &value) { for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { if(packed.second()(packed.first()[*curr].second, value)) { const auto index = *curr; *curr = packed.first()[*curr].first; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Returns the number of elements matching a value (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const value_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given value. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const value_type &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const value_type &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Finds an element that compares _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Returns a range containing all elements with a given value. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const value_type &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const value_type &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const value_type &value) const { return (find(value) != cend()); } /** * @brief Checks if the container contains an element that compares * _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &value) const { return (find(value) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given element. * @param value The value of the element to examine. * @return The bucket for the given element. */ [[nodiscard]] size_type bucket(const value_type &value) const { return value_to_bucket(value); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); packed.first()[pos].first = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the elements. * @return The function used to hash the elements. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare elements for equality. * @return The function used to compare elements for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt #endif // #include "core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP #include #include #include #include #include // #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif namespace entt { /** * @brief Function object to wrap `std::sort` in a class type. * * Unfortunately, `std::sort` cannot be passed as template argument to a class * template or a function template.
* This class fills the gap by wrapping some flavors of `std::sort` in a * function object. */ struct std_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @tparam Args Types of arguments to forward to the sort function. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. * @param args Arguments to forward to the sort function, if any. */ template, typename... Args> void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); } }; /*! @brief Function object for performing insertion sort. */ struct insertion_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. */ template> void operator()(It first, It last, Compare compare = Compare{}) const { if(first < last) { for(auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; for(; pre > first && compare(value, *(pre - 1)); --pre) { *pre = std::move(*(pre - 1)); } *pre = std::move(value); } } } }; /** * @brief Function object for performing LSD radix sort. * @tparam Bit Number of bits processed per pass. * @tparam N Maximum number of bits to sort. */ template struct radix_sort { static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given _getter_ to access the * actual data to be sorted. * * This implementation is inspired by the online book * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). * * @tparam It Type of random access iterator. * @tparam Getter Type of _getter_ function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param getter A valid _getter_ function object. */ template void operator()(It first, It last, Getter getter = Getter{}) const { if(first < last) { static constexpr auto mask = (1 << Bit) - 1; static constexpr auto buckets = 1 << Bit; static constexpr auto passes = N / Bit; using value_type = typename std::iterator_traits::value_type; std::vector aux(std::distance(first, last)); auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { std::size_t index[buckets]{}; std::size_t count[buckets]{}; for(auto it = from; it != to; ++it) { ++count[(getter(*it) >> start) & mask]; } for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { index[pos + 1u] = index[pos] + count[pos]; } for(auto it = from; it != to; ++it) { out[index[(getter(*it) >> start) & mask]++] = std::move(*it); } }; for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { part(first, last, aux.begin(), pass * Bit); part(aux.begin(), aux.end(), first, (pass + 1) * Bit); } if constexpr(passes & 1) { part(first, last, aux.begin(), (passes - 1) * Bit); std::move(aux.begin(), aux.end(), first); } } } }; } // namespace entt #endif // #include "core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "fwd.hpp" // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class any_operation : std::uint8_t { copy, move, transfer, assign, destroy, compare, get }; enum class any_policy : std::uint8_t { owner, ref, cref }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_any { using operation = internal::any_operation; using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); struct storage_type { alignas(Align) std::byte data[Len + !Len]; }; template static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; if constexpr(in_situ) { element = value.owner() ? reinterpret_cast(&value.storage) : static_cast(value.instance); } else { element = static_cast(value.instance); } switch(op) { case operation::copy: if constexpr(std::is_copy_constructible_v) { static_cast(const_cast(other))->initialize(*element); } break; case operation::move: if constexpr(in_situ) { if(value.owner()) { return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; } } return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); case operation::transfer: if constexpr(std::is_move_assignable_v) { *const_cast(element) = std::move(*static_cast(const_cast(other))); return other; } [[fallthrough]]; case operation::assign: if constexpr(std::is_copy_assignable_v) { *const_cast(element) = *static_cast(other); return other; } break; case operation::destroy: if constexpr(in_situ) { element->~Type(); } else if constexpr(std::is_array_v) { delete[] element; } else { delete element; } break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } case operation::get: return element; } return nullptr; } template void initialize([[maybe_unused]] Args &&...args) { info = &type_id>>(); if constexpr(!std::is_void_v) { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); } } } } basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, mode{pol} {} public: /*! @brief Size of the internal storage. */ static constexpr auto length = Len; /*! @brief Alignment requirement. */ static constexpr auto alignment = Align; /*! @brief Default constructor. */ constexpr basic_any() noexcept : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_any(std::in_place_type_t, Args &&...args) : instance{}, info{}, vtable{}, mode{policy::owner} { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, basic_any>>> basic_any(Type &&value) : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any &other) : basic_any{} { if(other.vtable) { other.vtable(operation::copy, other, this); } } /** * @brief Move constructor. * @param other The instance to move from. */ basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, mode{other.mode} { if(other.vtable) { other.vtable(operation::move, other, this); } } /*! @brief Frees the internal storage, whatever it means. */ ~basic_any() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This any object. */ basic_any &operator=(const basic_any &other) { reset(); if(other.vtable) { other.vtable(operation::copy, other, this); } return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This any object. */ basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { other.vtable(operation::move, other, this); info = other.info; vtable = other.vtable; mode = other.mode; } return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This any object. */ template std::enable_if_t, basic_any>, basic_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] const type_info &type() const noexcept { return *info; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data() noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data(const type_info &req) noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&...args) { reset(); initialize(std::forward(args)...); } /** * @brief Assigns a value to the contained object without replacing it. * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } return false; } /*! @copydoc assign */ bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); } else { return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); } } return false; } /*! @brief Destroys contained object */ void reset() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; } /** * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } return (!vtable && !other.vtable); } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return True if the two objects differ in their content, false otherwise. */ [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { return !(*this == other); } /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } /** * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } private: union { const void *instance; storage_type storage; }; const type_info *info; vtable_type *vtable; policy mode; }; /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param data Target any object. * @return The element converted to the requested type. */ template Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); } else { return any_cast(data); } } else { auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } } /*! @copydoc any_cast */ template const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); } else { const auto &info = type_id>(); return static_cast(data->data(info)); } } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt #endif // #include "core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "core/enum.hpp" #ifndef ENTT_CORE_ENUM_HPP #define ENTT_CORE_ENUM_HPP #include namespace entt { /** * @brief Enable bitmask support for enum classes. * @tparam Type The enum type for which to enable bitmask support. */ template struct enum_as_bitmask: std::false_type {}; /*! @copydoc enum_as_bitmask */ template struct enum_as_bitmask>: std::is_enum {}; /** * @brief Helper variable template. * @tparam Type The enum class type for which to enable bitmask support. */ template inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; } // namespace entt /** * @brief Operator available for enums for which bitmask support is enabled. * @tparam Type Enum class type. * @param lhs The first value to use. * @param rhs The second value to use. * @return The result of invoking the operator on the underlying types of the * two values provided. */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator|(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator&(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator^(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } /** * @brief Operator available for enums for which bitmask support is enabled. * @tparam Type Enum class type. * @param value The value to use. * @return The result of invoking the operator on the underlying types of the * value provided. */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator~(const Type value) noexcept { return static_cast(~static_cast>(value)); } /*! @copydoc operator~ */ template [[nodiscard]] constexpr std::enable_if_t, bool> operator!(const Type value) noexcept { return !static_cast>(value); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator|=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs | rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator&=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs & rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator^=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs ^ rhs)); } #endif // #include "core/family.hpp" #ifndef ENTT_CORE_FAMILY_HPP #define ENTT_CORE_FAMILY_HPP // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Dynamic identifier generator. * * Utility class template that can be used to assign unique identifiers to types * at runtime. Use different specializations to create separate sets of * identifiers. */ template class family { inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; public: /*! @brief Unsigned integer type. */ using value_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type inline static const value_type value = identifier++; }; } // namespace entt #endif // #include "core/hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif // #include "core/ident.hpp" #ifndef ENTT_CORE_IDENT_HPP #define ENTT_CORE_IDENT_HPP #include #include #include // #include "fwd.hpp" // #include "type_traits.hpp" namespace entt { /** * @brief Type integral identifiers. * @tparam Type List of types for which to generate identifiers. */ template class ident { template [[nodiscard]] static constexpr id_type get(std::index_sequence) noexcept { static_assert((std::is_same_v || ...), "Invalid type"); return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); } public: /*! @brief Unsigned integer type. */ using value_type = id_type; /*! @brief Statically generated unique identifier for the given type. */ template static constexpr value_type value = get>(std::index_sequence_for{}); }; } // namespace entt #endif // #include "core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "core/monostate.hpp" #ifndef ENTT_CORE_MONOSTATE_HPP #define ENTT_CORE_MONOSTATE_HPP // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Minimal implementation of the monostate pattern. * * A minimal, yet complete configuration system built on top of the monostate * pattern. Thread safe by design, it works only with basic types like `int`s or * `bool`s.
* Multiple types and therefore more than one value can be associated with a * single key. Because of this, users must pay attention to use the same type * both during an assignment and when they try to read back their data. * Otherwise, they can incur in unexpected results. */ template struct monostate { /** * @brief Assigns a value of a specific type to a given key. * @tparam Type Type of the value to assign. * @param val User data to assign to the given key. */ template void operator=(Type val) const noexcept { value = val; } /** * @brief Gets a value of a specific type for a given key. * @tparam Type Type of the value to get. * @return Stored value, if any. */ template operator Type() const noexcept { return value; } private: template inline static ENTT_MAYBE_ATOMIC(Type) value{}; }; /** * @brief Helper variable template. * @tparam Value Value used to differentiate between different variables. */ template inline monostate monostate_v = {}; } // namespace entt #endif // #include "core/tuple.hpp" #ifndef ENTT_CORE_TUPLE_HPP #define ENTT_CORE_TUPLE_HPP #include #include #include namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct is_tuple_impl: std::false_type {}; template struct is_tuple_impl>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Provides the member constant `value` to true if a given type is a * tuple, false otherwise. * @tparam Type The type to test. */ template struct is_tuple: internal::is_tuple_impl> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_tuple_v = is_tuple::value; /** * @brief Utility function to unwrap tuples of a single element. * @tparam Type Tuple type of any sizes. * @param value A tuple object of the given type. * @return The tuple itself if it contains more than one element, the first * element otherwise. */ template constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { if constexpr(std::tuple_size_v> == 1u) { return std::get<0>(std::forward(value)); } else { return std::forward(value); } } /** * @brief Utility class to forward-and-apply tuple objects. * @tparam Func Type of underlying invocable object. */ template struct forward_apply: private Func { /** * @brief Constructs a forward-and-apply object. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) : Func{std::forward(args)...} {} /** * @brief Forwards and applies the arguments with the underlying function. * @tparam Type Tuple-like type to forward to the underlying function. * @param args Parameters to forward to the underlying function. * @return Return value of the underlying function, if any. */ template constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval(), args))) { return std::apply(static_cast(*this), std::forward(args)); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { return std::apply(static_cast(*this), std::forward(args)); } }; /** * @brief Deduction guide. * @tparam Func Type of underlying invocable object. */ template forward_apply(Func) -> forward_apply>>; } // namespace entt #endif // #include "core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "fwd.hpp" // #include "hashed_string.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "entity/component.hpp" #ifndef ENTT_ENTITY_COMPONENT_HPP #define ENTT_ENTITY_COMPONENT_HPP #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; template struct in_place_delete> : std::true_type {}; template struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; template struct page_size>> : std::integral_constant {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Common way to access various properties of components. * @tparam Type Type of component. */ template struct component_traits { static_assert(std::is_same_v, Type>, "Unsupported type"); /*! @brief Component type. */ using type = Type; /*! @brief Pointer stability, default is `false`. */ static constexpr bool in_place_delete = internal::in_place_delete::value; /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ static constexpr std::size_t page_size = internal::page_size::value; }; /** * @brief Helper variable template. * @tparam Type Type of component. */ template inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); } // namespace entt #endif // #include "entity/entity.hpp" #ifndef ENTT_ENTITY_ENTITY_HPP #define ENTT_ENTITY_ENTITY_HPP #include #include #include // #include "../config/config.h" // #include "fwd.hpp" #ifndef ENTT_ENTITY_FWD_HPP #define ENTT_ENTITY_FWD_HPP #include #include // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /*! @brief Default entity identifier. */ enum class entity : id_type {}; template> class basic_sparse_set; template, typename = void> class basic_storage; template class sigh_storage_mixin; /** * @brief Provides a common way to define storage types. * @tparam Type Storage value type. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template, typename = void> struct storage_type { /*! @brief Type-to-storage conversion result. */ using type = sigh_storage_mixin>; }; /** * @brief Helper type. * @tparam Args Arguments to forward. */ template using storage_type_t = typename storage_type::type; /** * Type-to-storage conversion utility that preserves constness. * @tparam Type Storage value type, eventually const. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template>> struct storage_for { /*! @brief Type-to-storage conversion result. */ using type = constness_as_t, Entity, Allocator>, Type>; }; /** * @brief Helper type. * @tparam Args Arguments to forward. */ template using storage_for_t = typename storage_for::type; template> class basic_registry; template class basic_view; template> class basic_runtime_view; template class basic_group; template class basic_observer; template class basic_organizer; template struct basic_handle; template class basic_snapshot; template class basic_snapshot_loader; template class basic_continuous_loader; /** * @brief Alias for exclusion lists. * @tparam Type List of types. */ template using exclude_t = type_list; /** * @brief Variable template for exclusion lists. * @tparam Type List of types. */ template inline constexpr exclude_t exclude{}; /** * @brief Alias for lists of observed components. * @tparam Type List of types. */ template using get_t = type_list; /** * @brief Variable template for lists of observed components. * @tparam Type List of types. */ template inline constexpr get_t get{}; /** * @brief Alias for lists of owned components. * @tparam Type List of types. */ template using owned_t = type_list; /** * @brief Variable template for lists of owned components. * @tparam Type List of types. */ template inline constexpr owned_t owned{}; /*! @brief Alias declaration for the most common use case. */ using sparse_set = basic_sparse_set<>; /** * @brief Alias declaration for the most common use case. * @tparam Type Type of objects assigned to the entities. */ template using storage = basic_storage; /*! @brief Alias declaration for the most common use case. */ using registry = basic_registry<>; /*! @brief Alias declaration for the most common use case. */ using observer = basic_observer; /*! @brief Alias declaration for the most common use case. */ using organizer = basic_organizer; /*! @brief Alias declaration for the most common use case. */ using handle = basic_handle; /*! @brief Alias declaration for the most common use case. */ using const_handle = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using handle_view = basic_handle; /** * @brief Alias declaration for the most common use case. * @tparam Args Other template parameters. */ template using const_handle_view = basic_handle; /*! @brief Alias declaration for the most common use case. */ using snapshot = basic_snapshot; /*! @brief Alias declaration for the most common use case. */ using snapshot_loader = basic_snapshot_loader; /*! @brief Alias declaration for the most common use case. */ using continuous_loader = basic_continuous_loader; /** * @brief Alias declaration for the most common use case. * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. */ template> using view = basic_view, type_list_transform_t>; /*! @brief Alias declaration for the most common use case. */ using runtime_view = basic_runtime_view; /*! @brief Alias declaration for the most common use case. */ using const_runtime_view = basic_runtime_view; /** * @brief Alias declaration for the most common use case. * @tparam Owned Types of storage _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ template using group = basic_group, type_list_transform_t, type_list_transform_t>; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct entt_traits; template struct entt_traits>> : entt_traits> {}; template struct entt_traits>> : entt_traits {}; template<> struct entt_traits { using entity_type = std::uint32_t; using version_type = std::uint16_t; static constexpr entity_type entity_mask = 0xFFFFF; static constexpr entity_type version_mask = 0xFFF; static constexpr std::size_t entity_shift = 20u; }; template<> struct entt_traits { using entity_type = std::uint64_t; using version_type = std::uint32_t; static constexpr entity_type entity_mask = 0xFFFFFFFF; static constexpr entity_type version_mask = 0xFFFFFFFF; static constexpr std::size_t entity_shift = 32u; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Entity traits. * @tparam Type Type of identifier. */ template class entt_traits: internal::entt_traits { using base_type = internal::entt_traits; public: /*! @brief Value type. */ using value_type = Type; /*! @brief Underlying entity type. */ using entity_type = typename base_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename base_type::version_type; /*! @brief Reserved identifier. */ static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift); /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ static constexpr auto page_size = ENTT_SPARSE_PAGE; /** * @brief Converts an entity to its underlying type. * @param value The value to convert. * @return The integral representation of the given value. */ [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { return static_cast(value); } /** * @brief Returns the entity part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the entity part. */ [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { return (to_integral(value) & base_type::entity_mask); } /** * @brief Returns the version part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the version part. */ [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { return (to_integral(value) >> base_type::entity_shift); } /** * @brief Constructs an identifier from its parts. * * If the version part is not provided, a tombstone is returned.
* If the entity part is not provided, a null identifier is returned. * * @param entity The entity part of the identifier. * @param version The version part of the identifier. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { return value_type{(entity & base_type::entity_mask) | (static_cast(version) << base_type::entity_shift)}; } /** * @brief Combines two identifiers in a single one. * * The returned identifier is a copy of the first element except for its * version, which is taken from the second element. * * @param lhs The identifier from which to take the entity part. * @param rhs The identifier from which to take the version part. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { constexpr auto mask = (base_type::version_mask << base_type::entity_shift); return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; } }; /** * @copydoc entt_traits::to_integral * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { return entt_traits::to_integral(value); } /** * @copydoc entt_traits::to_entity * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { return entt_traits::to_entity(value); } /** * @copydoc entt_traits::to_version * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { return entt_traits::to_version(value); } /*! @brief Null object for all identifiers. */ struct null_t { /** * @brief Converts the null object to identifiers of any type. * @tparam Entity Type of identifier. * @return The null representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } /** * @brief Compares two null objects. * @param other A null object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { return true; } /** * @brief Compares two null objects. * @param other A null object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { return false; } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using entity_traits = entt_traits; return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A null object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { return other.operator==(entity); } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A null object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { return !(other == entity); } /*! @brief Tombstone object for all identifiers. */ struct tombstone_t { /** * @brief Converts the tombstone object to identifiers of any type. * @tparam Entity Type of identifier. * @return The tombstone representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { return true; } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { return false; } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using entity_traits = entt_traits; return entity_traits::to_version(entity) == entity_traits::to_version(*this); } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A tombstone object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { return other.operator==(entity); } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A tombstone object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { return !(other == entity); } /** * @brief Compile-time constant for null entities. * * There exist implicit conversions from this variable to identifiers of any * allowed type. Similarly, there exist comparison operators between the null * entity and any other identifier. */ inline constexpr null_t null{}; /** * @brief Compile-time constant for tombstone entities. * * There exist implicit conversions from this variable to identifiers of any * allowed type. Similarly, there exist comparison operators between the * tombstone entity and any other identifier. */ inline constexpr tombstone_t tombstone{}; } // namespace entt #endif // #include "entity/group.hpp" #ifndef ENTT_ENTITY_GROUP_HPP #define ENTT_ENTITY_GROUP_HPP #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/type_traits.hpp" // #include "component.hpp" #ifndef ENTT_ENTITY_COMPONENT_HPP #define ENTT_ENTITY_COMPONENT_HPP #include #include // #include "../config/config.h" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; template struct in_place_delete> : std::true_type {}; template struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; template struct page_size>> : std::integral_constant {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Common way to access various properties of components. * @tparam Type Type of component. */ template struct component_traits { static_assert(std::is_same_v, Type>, "Unsupported type"); /*! @brief Component type. */ using type = Type; /*! @brief Pointer stability, default is `false`. */ static constexpr bool in_place_delete = internal::in_place_delete::value; /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ static constexpr std::size_t page_size = internal::page_size::value; }; /** * @brief Helper variable template. * @tparam Type Type of component. */ template inline constexpr bool ignore_as_empty_v = (std::is_void_v || component_traits::page_size == 0u); } // namespace entt #endif // #include "entity.hpp" #ifndef ENTT_ENTITY_ENTITY_HPP #define ENTT_ENTITY_ENTITY_HPP #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct entt_traits; template struct entt_traits>> : entt_traits> {}; template struct entt_traits>> : entt_traits {}; template<> struct entt_traits { using entity_type = std::uint32_t; using version_type = std::uint16_t; static constexpr entity_type entity_mask = 0xFFFFF; static constexpr entity_type version_mask = 0xFFF; static constexpr std::size_t entity_shift = 20u; }; template<> struct entt_traits { using entity_type = std::uint64_t; using version_type = std::uint32_t; static constexpr entity_type entity_mask = 0xFFFFFFFF; static constexpr entity_type version_mask = 0xFFFFFFFF; static constexpr std::size_t entity_shift = 32u; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Entity traits. * @tparam Type Type of identifier. */ template class entt_traits: internal::entt_traits { using base_type = internal::entt_traits; public: /*! @brief Value type. */ using value_type = Type; /*! @brief Underlying entity type. */ using entity_type = typename base_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename base_type::version_type; /*! @brief Reserved identifier. */ static constexpr entity_type reserved = base_type::entity_mask | (base_type::version_mask << base_type::entity_shift); /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ static constexpr auto page_size = ENTT_SPARSE_PAGE; /** * @brief Converts an entity to its underlying type. * @param value The value to convert. * @return The integral representation of the given value. */ [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { return static_cast(value); } /** * @brief Returns the entity part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the entity part. */ [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { return (to_integral(value) & base_type::entity_mask); } /** * @brief Returns the version part once converted to the underlying type. * @param value The value to convert. * @return The integral representation of the version part. */ [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { return (to_integral(value) >> base_type::entity_shift); } /** * @brief Constructs an identifier from its parts. * * If the version part is not provided, a tombstone is returned.
* If the entity part is not provided, a null identifier is returned. * * @param entity The entity part of the identifier. * @param version The version part of the identifier. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { return value_type{(entity & base_type::entity_mask) | (static_cast(version) << base_type::entity_shift)}; } /** * @brief Combines two identifiers in a single one. * * The returned identifier is a copy of the first element except for its * version, which is taken from the second element. * * @param lhs The identifier from which to take the entity part. * @param rhs The identifier from which to take the version part. * @return A properly constructed identifier. */ [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { constexpr auto mask = (base_type::version_mask << base_type::entity_shift); return value_type{(lhs & base_type::entity_mask) | (rhs & mask)}; } }; /** * @copydoc entt_traits::to_integral * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { return entt_traits::to_integral(value); } /** * @copydoc entt_traits::to_entity * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { return entt_traits::to_entity(value); } /** * @copydoc entt_traits::to_version * @tparam Entity The value type. */ template [[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { return entt_traits::to_version(value); } /*! @brief Null object for all identifiers. */ struct null_t { /** * @brief Converts the null object to identifiers of any type. * @tparam Entity Type of identifier. * @return The null representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } /** * @brief Compares two null objects. * @param other A null object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { return true; } /** * @brief Compares two null objects. * @param other A null object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { return false; } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using entity_traits = entt_traits; return entity_traits::to_entity(entity) == entity_traits::to_entity(*this); } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A null object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { return other.operator==(entity); } /** * @brief Compares a null object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A null object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { return !(other == entity); } /*! @brief Tombstone object for all identifiers. */ struct tombstone_t { /** * @brief Converts the tombstone object to identifiers of any type. * @tparam Entity Type of identifier. * @return The tombstone representation for the given type. */ template [[nodiscard]] constexpr operator Entity() const noexcept { using entity_traits = entt_traits; return entity_traits::combine(entity_traits::reserved, entity_traits::reserved); } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return True in all cases. */ [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { return true; } /** * @brief Compares two tombstone objects. * @param other A tombstone object. * @return False in all cases. */ [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { return false; } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { using entity_traits = entt_traits; return entity_traits::to_version(entity) == entity_traits::to_version(*this); } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { return !(entity == *this); } }; /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A tombstone object yet to be converted. * @return False if the two elements differ, true otherwise. */ template [[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { return other.operator==(entity); } /** * @brief Compares a tombstone object and an identifier of any type. * @tparam Entity Type of identifier. * @param entity Identifier with which to compare. * @param other A tombstone object yet to be converted. * @return True if the two elements differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { return !(other == entity); } /** * @brief Compile-time constant for null entities. * * There exist implicit conversions from this variable to identifiers of any * allowed type. Similarly, there exist comparison operators between the null * entity and any other identifier. */ inline constexpr null_t null{}; /** * @brief Compile-time constant for tombstone entities. * * There exist implicit conversions from this variable to identifiers of any * allowed type. Similarly, there exist comparison operators between the * tombstone entity and any other identifier. */ inline constexpr tombstone_t tombstone{}; } // namespace entt #endif // #include "fwd.hpp" // #include "sparse_set.hpp" #ifndef ENTT_ENTITY_SPARSE_SET_HPP #define ENTT_ENTITY_SPARSE_SET_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" #ifndef ENTT_CORE_ALGORITHM_HPP #define ENTT_CORE_ALGORITHM_HPP #include #include #include #include #include // #include "utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif namespace entt { /** * @brief Function object to wrap `std::sort` in a class type. * * Unfortunately, `std::sort` cannot be passed as template argument to a class * template or a function template.
* This class fills the gap by wrapping some flavors of `std::sort` in a * function object. */ struct std_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @tparam Args Types of arguments to forward to the sort function. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. * @param args Arguments to forward to the sort function, if any. */ template, typename... Args> void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); } }; /*! @brief Function object for performing insertion sort. */ struct insertion_sort { /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given binary comparison function. * * @tparam It Type of random access iterator. * @tparam Compare Type of comparison function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param compare A valid comparison function object. */ template> void operator()(It first, It last, Compare compare = Compare{}) const { if(first < last) { for(auto it = first + 1; it < last; ++it) { auto value = std::move(*it); auto pre = it; for(; pre > first && compare(value, *(pre - 1)); --pre) { *pre = std::move(*(pre - 1)); } *pre = std::move(value); } } } }; /** * @brief Function object for performing LSD radix sort. * @tparam Bit Number of bits processed per pass. * @tparam N Maximum number of bits to sort. */ template struct radix_sort { static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); /** * @brief Sorts the elements in a range. * * Sorts the elements in a range using the given _getter_ to access the * actual data to be sorted. * * This implementation is inspired by the online book * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). * * @tparam It Type of random access iterator. * @tparam Getter Type of _getter_ function object. * @param first An iterator to the first element of the range to sort. * @param last An iterator past the last element of the range to sort. * @param getter A valid _getter_ function object. */ template void operator()(It first, It last, Getter getter = Getter{}) const { if(first < last) { static constexpr auto mask = (1 << Bit) - 1; static constexpr auto buckets = 1 << Bit; static constexpr auto passes = N / Bit; using value_type = typename std::iterator_traits::value_type; std::vector aux(std::distance(first, last)); auto part = [getter = std::move(getter)](auto from, auto to, auto out, auto start) { std::size_t index[buckets]{}; std::size_t count[buckets]{}; for(auto it = from; it != to; ++it) { ++count[(getter(*it) >> start) & mask]; } for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { index[pos + 1u] = index[pos] + count[pos]; } for(auto it = from; it != to; ++it) { out[index[(getter(*it) >> start) & mask]++] = std::move(*it); } }; for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { part(first, last, aux.begin(), pass * Bit); part(aux.begin(), aux.end(), first, (pass + 1) * Bit); } if constexpr(passes & 1) { part(first, last, aux.begin(), (passes - 1) * Bit); std::move(aux.begin(), aux.end(), first); } } } }; } // namespace entt #endif // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include // #include "../config/config.h" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "fwd.hpp" // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class any_operation : std::uint8_t { copy, move, transfer, assign, destroy, compare, get }; enum class any_policy : std::uint8_t { owner, ref, cref }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_any { using operation = internal::any_operation; using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); struct storage_type { alignas(Align) std::byte data[Len + !Len]; }; template static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; if constexpr(in_situ) { element = value.owner() ? reinterpret_cast(&value.storage) : static_cast(value.instance); } else { element = static_cast(value.instance); } switch(op) { case operation::copy: if constexpr(std::is_copy_constructible_v) { static_cast(const_cast(other))->initialize(*element); } break; case operation::move: if constexpr(in_situ) { if(value.owner()) { return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; } } return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); case operation::transfer: if constexpr(std::is_move_assignable_v) { *const_cast(element) = std::move(*static_cast(const_cast(other))); return other; } [[fallthrough]]; case operation::assign: if constexpr(std::is_copy_assignable_v) { *const_cast(element) = *static_cast(other); return other; } break; case operation::destroy: if constexpr(in_situ) { element->~Type(); } else if constexpr(std::is_array_v) { delete[] element; } else { delete element; } break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } case operation::get: return element; } return nullptr; } template void initialize([[maybe_unused]] Args &&...args) { info = &type_id>>(); if constexpr(!std::is_void_v) { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); } } } } basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, mode{pol} {} public: /*! @brief Size of the internal storage. */ static constexpr auto length = Len; /*! @brief Alignment requirement. */ static constexpr auto alignment = Align; /*! @brief Default constructor. */ constexpr basic_any() noexcept : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_any(std::in_place_type_t, Args &&...args) : instance{}, info{}, vtable{}, mode{policy::owner} { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, basic_any>>> basic_any(Type &&value) : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any &other) : basic_any{} { if(other.vtable) { other.vtable(operation::copy, other, this); } } /** * @brief Move constructor. * @param other The instance to move from. */ basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, mode{other.mode} { if(other.vtable) { other.vtable(operation::move, other, this); } } /*! @brief Frees the internal storage, whatever it means. */ ~basic_any() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This any object. */ basic_any &operator=(const basic_any &other) { reset(); if(other.vtable) { other.vtable(operation::copy, other, this); } return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This any object. */ basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { other.vtable(operation::move, other, this); info = other.info; vtable = other.vtable; mode = other.mode; } return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This any object. */ template std::enable_if_t, basic_any>, basic_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] const type_info &type() const noexcept { return *info; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data() noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data(const type_info &req) noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&...args) { reset(); initialize(std::forward(args)...); } /** * @brief Assigns a value to the contained object without replacing it. * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } return false; } /*! @copydoc assign */ bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); } else { return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); } } return false; } /*! @brief Destroys contained object */ void reset() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; } /** * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } return (!vtable && !other.vtable); } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return True if the two objects differ in their content, false otherwise. */ [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { return !(*this == other); } /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } /** * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } private: union { const void *instance; storage_type storage; }; const type_info *info; vtable_type *vtable; policy mode; }; /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param data Target any object. * @return The element converted to the requested type. */ template Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); } else { return any_cast(data); } } else { auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } } /*! @copydoc any_cast */ template const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); } else { const auto &info = type_id>(); return static_cast(data->data(info)); } } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "fwd.hpp" // #include "hashed_string.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct sparse_set_iterator final { using value_type = typename Container::value_type; using pointer = typename Container::const_pointer; using reference = typename Container::const_reference; using difference_type = typename Container::difference_type; using iterator_category = std::random_access_iterator_tag; constexpr sparse_set_iterator() noexcept : packed{}, offset{} {} constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept : packed{std::addressof(ref)}, offset{idx} {} constexpr sparse_set_iterator &operator++() noexcept { return --offset, *this; } constexpr sparse_set_iterator operator++(int) noexcept { sparse_set_iterator orig = *this; return ++(*this), orig; } constexpr sparse_set_iterator &operator--() noexcept { return ++offset, *this; } constexpr sparse_set_iterator operator--(int) noexcept { sparse_set_iterator orig = *this; return operator--(), orig; } constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { sparse_set_iterator copy = *this; return (copy += value); } constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return packed->data()[index() - value]; } [[nodiscard]] constexpr pointer operator->() const noexcept { return packed->data() + index(); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } private: const Container *packed; difference_type offset; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template [[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template [[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template [[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @brief Sparse set deletion policy. */ enum class deletion_policy : std::uint8_t { /*! @brief Swap-and-pop deletion policy. */ swap_and_pop = 0u, /*! @brief In-place deletion policy. */ in_place = 1u }; /** * @brief Basic sparse set implementation. * * Sparse set or packed array or whatever is the name users give it.
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
* This is largely used by the registry to offer users the fastest access ever * to the components. Views and groups in general are almost entirely designed * around sparse sets. * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_sparse_set { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector; using entity_traits = entt_traits; [[nodiscard]] auto sparse_ptr(const Entity entt) const { const auto pos = static_cast(entity_traits::to_entity(entt)); const auto page = pos / entity_traits::page_size; return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr; } [[nodiscard]] auto &sparse_ref(const Entity entt) const { ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); const auto pos = static_cast(entity_traits::to_entity(entt)); return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)]; } [[nodiscard]] auto &assure_at_least(const Entity entt) { const auto pos = static_cast(entity_traits::to_entity(entt)); const auto page = pos / entity_traits::page_size; if(!(page < sparse.size())) { sparse.resize(page + 1u, nullptr); } if(!sparse[page]) { auto page_allocator{packed.get_allocator()}; sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size); std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null); } auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; ENTT_ASSERT(elem == null, "Slot not available"); return elem; } void release_sparse_pages() { auto page_allocator{packed.get_allocator()}; for(auto &&page: sparse) { if(page != nullptr) { std::destroy(page, page + entity_traits::page_size); alloc_traits::deallocate(page_allocator, page, entity_traits::page_size); page = nullptr; } } } private: virtual const void *get_at(const std::size_t) const { return nullptr; } virtual void swap_at(const std::size_t, const std::size_t) {} virtual void move_element(const std::size_t, const std::size_t) {} protected: /*! @brief Random access iterator type. */ using basic_iterator = internal::sparse_set_iterator; /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. */ void swap_and_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched"); auto &self = sparse_ref(*it); const auto entt = entity_traits::to_entity(self); sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); packed[static_cast(entt)] = packed.back(); // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((packed.back() = null, true), ""); // lazy self-assignment guard self = null; packed.pop_back(); } /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. */ void in_place_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched"); const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null)); packed[static_cast(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); } protected: /** * @brief Erases entities from a sparse set. * @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. */ virtual void pop(basic_iterator first, basic_iterator last) { if(mode == deletion_policy::swap_and_pop) { for(; first != last; ++first) { swap_and_pop(first); } } else { for(; first != last; ++first) { in_place_pop(first); } } } /** * @brief Assigns an entity to a sparse set. * @param entt A valid identifier. * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { ENTT_ASSERT(!contains(entt), "Set already contains entity"); if(auto &elem = assure_at_least(entt); free_list == null || force_back) { packed.push_back(entt); elem = entity_traits::combine(static_cast(packed.size() - 1u), entity_traits::to_integral(entt)); return begin(); } else { const auto pos = static_cast(entity_traits::to_entity(free_list)); elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt)); free_list = std::exchange(packed[pos], entt); return --(end() - pos); } } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ using entity_type = typename entity_traits::value_type; /*! @brief Underlying version type. */ using version_type = typename entity_traits::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ using iterator = basic_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = reverse_iterator; /*! @brief Default constructor. */ basic_sparse_set() : basic_sparse_set{type_id()} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit basic_sparse_set(const allocator_type &allocator) : basic_sparse_set{type_id(), deletion_policy::swap_and_pop, allocator} {} /** * @brief Constructs an empty container with the given policy and allocator. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) : basic_sparse_set{type_id(), pol, allocator} {} /** * @brief Constructs an empty container with the given value type, policy * and allocator. * @param value Returned value type, if any. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) : sparse{allocator}, packed{allocator}, info{&value}, free_list{tombstone}, mode{pol} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_sparse_set(basic_sparse_set &&other) noexcept : sparse{std::move(other.sparse)}, packed{std::move(other.packed)}, info{other.info}, free_list{std::exchange(other.free_list, tombstone)}, mode{other.mode} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept : sparse{std::move(other.sparse), allocator}, packed{std::move(other.packed), allocator}, info{other.info}, free_list{std::exchange(other.free_list, tombstone)}, mode{other.mode} { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); } /*! @brief Default destructor. */ virtual ~basic_sparse_set() { release_sparse_pages(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); release_sparse_pages(); sparse = std::move(other.sparse); packed = std::move(other.packed); info = other.info; free_list = std::exchange(other.free_list, tombstone); mode = other.mode; return *this; } /** * @brief Exchanges the contents with those of a given sparse set. * @param other Sparse set to exchange the content with. */ void swap(basic_sparse_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(info, other.info); swap(free_list, other.free_list); swap(mode, other.mode); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return packed.get_allocator(); } /** * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ [[nodiscard]] deletion_policy policy() const noexcept { return mode; } /** * @brief Increases the capacity of a sparse set. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ virtual void reserve(const size_type cap) { packed.reserve(cap); } /** * @brief Returns the number of elements that a sparse set has currently * allocated space for. * @return Capacity of the sparse set. */ [[nodiscard]] virtual size_type capacity() const noexcept { return packed.capacity(); } /*! @brief Requests the removal of unused capacity. */ virtual void shrink_to_fit() { packed.shrink_to_fit(); } /** * @brief Returns the extent of a sparse set. * * The extent of a sparse set is also the size of the internal sparse array. * There is no guarantee that the internal packed array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const noexcept { return sparse.size() * entity_traits::page_size; } /** * @brief Returns the number of elements in a sparse set. * * The number of elements is also the size of the internal packed array. * There is no guarantee that the internal sparse array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Number of elements. */ [[nodiscard]] size_type size() const noexcept { return packed.size(); } /** * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.empty(); } /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ [[nodiscard]] pointer data() const noexcept { return packed.data(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first entity of the internal packed * array. If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the sparse set. */ [[nodiscard]] const_iterator begin() const noexcept { const auto pos = static_cast(packed.size()); return iterator{packed, pos}; } /*! @copydoc begin */ [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last entity in * a sparse set. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the element following the last entity of a sparse * set. */ [[nodiscard]] iterator end() const noexcept { return iterator{packed, {}}; } /*! @copydoc end */ [[nodiscard]] const_iterator cend() const noexcept { return end(); } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first entity of the reversed internal * packed array. If the sparse set is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. */ [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return std::make_reverse_iterator(end()); } /*! @copydoc rbegin */ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last entity in * the reversed sparse set. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last entity of the * reversed sparse set. */ [[nodiscard]] reverse_iterator rend() const noexcept { return std::make_reverse_iterator(begin()); } /*! @copydoc rend */ [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? --(end() - index(entt)) : end(); } /** * @brief Checks if a sparse set contains an entity. * @param entt A valid identifier. * @return True if the sparse set contains the entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto cap = entity_traits::to_entity(null); // testing versions permits to avoid accessing the packed array return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap); } /** * @brief Returns the contained version for an identifier. * @param entt A valid identifier. * @return The version for the given identifier if present, the tombstone * version otherwise. */ [[nodiscard]] version_type current(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto fallback = entity_traits::to_version(tombstone); return elem ? entity_traits::to_version(*elem) : fallback; } /** * @brief Returns the position of an entity in a sparse set. * * @warning * Attempting to get the position of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid identifier. * @return The position of the entity in the sparse set. */ [[nodiscard]] size_type index(const entity_type entt) const noexcept { ENTT_ASSERT(contains(entt), "Set does not contain entity"); return static_cast(entity_traits::to_entity(sparse_ref(entt))); } /** * @brief Returns the entity at specified location, with bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ [[nodiscard]] entity_type at(const size_type pos) const noexcept { return pos < packed.size() ? packed[pos] : null; } /** * @brief Returns the entity at specified location, without bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location. */ [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); return packed[pos]; } /** * @brief Returns the element assigned to an entity, if any. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results * in undefined behavior. * * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ [[nodiscard]] const void *get(const entity_type entt) const noexcept { return get_at(index(entt)); } /*! @copydoc get */ [[nodiscard]] void *get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Assigns an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid identifier. * @param value Optional opaque value to forward to mixins, if any. * @return Iterator pointing to the emplaced element in case of success, the * `end()` iterator otherwise. */ iterator emplace(const entity_type entt, const void *value = nullptr) { return try_emplace(entt, false, value); } /** * @brief Bump the version number of an entity. * * @warning * Attempting to bump the version of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid identifier. */ void bump(const entity_type entt) { auto &entity = sparse_ref(entt); ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt)); packed[static_cast(entity_traits::to_entity(entity))] = entt; } /** * @brief Assigns one or more entities to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return Iterator pointing to the first element inserted in case of * success, the `end()` iterator otherwise. */ template iterator insert(It first, It last) { for(auto it = first; it != last; ++it) { try_emplace(*it, true); } return first == last ? end() : find(*first); } /** * @brief Erases an entity from a sparse set. * * @warning * Attempting to erase an entity that doesn't belong to the sparse set * results in undefined behavior. * * @param entt A valid identifier. */ void erase(const entity_type entt) { const auto it = --(end() - index(entt)); pop(it, it + 1u); } /** * @brief Erases entities from a set. * * @sa erase * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void erase(It first, It last) { if constexpr(std::is_same_v) { pop(first, last); } else { for(; first != last; ++first) { erase(*first); } } } /** * @brief Removes an entity from a sparse set if it exists. * @param entt A valid identifier. * @return True if the entity is actually removed, false otherwise. */ bool remove(const entity_type entt) { return contains(entt) && (erase(entt), true); } /** * @brief Removes entities from a sparse set if they exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return The number of entities actually removed. */ template size_type remove(It first, It last) { size_type count{}; for(; first != last; ++first) { count += remove(*first); } return count; } /*! @brief Removes all tombstones from the packed array of a sparse set. */ void compact() { size_type from = packed.size(); for(; from && packed[from - 1u] == tombstone; --from) {} for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) { if(const size_type to = entity_traits::to_entity(*it); to < from) { --from; move_element(from, to); using std::swap; swap(packed[from], packed[to]); const auto entity = static_cast(to); sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to])); *it = entity_traits::combine(static_cast(from), entity_traits::reserved); for(; from && packed[from - 1u] == tombstone; --from) {} } } free_list = tombstone; packed.resize(from); } /** * @brief Swaps two entities in a sparse set. * * For what it's worth, this function affects both the internal sparse array * and the internal packed array. Users should not care of that anyway. * * @warning * Attempting to swap entities that don't belong to the sparse set results * in undefined behavior. * * @param lhs A valid identifier. * @param rhs A valid identifier. */ void swap_elements(const entity_type lhs, const entity_type rhs) { ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); auto &entt = sparse_ref(lhs); auto &other = sparse_ref(rhs); const auto from = entity_traits::to_entity(entt); const auto to = entity_traits::to_entity(other); // basic no-leak guarantee (with invalid state) if swapping throws swap_at(static_cast(from), static_cast(to)); entt = entity_traits::combine(to, entity_traits::to_integral(packed[from])); other = entity_traits::combine(from, entity_traits::to_integral(packed[to])); using std::swap; swap(packed[from], packed[to]); } /** * @brief Sort the first count elements according to the given comparison * function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to the following: * * @code{.cpp} * bool(const Entity, const Entity); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported"); algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward(args)...); for(size_type pos{}; pos < length; ++pos) { auto curr = pos; auto next = index(packed[curr]); while(curr != next) { const auto idx = index(packed[next]); const auto entt = packed[curr]; swap_at(next, idx); const auto entity = static_cast(curr); sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr])); curr = std::exchange(next, idx); } } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { compact(); sort_n(packed.size(), std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally * according to the order they have in `other`. All the other entities goes * to the end of the list and there are no guarantees on their order.
* In other terms, this function can be used to impose the same order on two * sets by using one of them as a master and the other one as a slave. * * Iterating the sparse set with a couple of iterators returns elements in * the expected order after a call to `respect`. See `begin` and `end` for * more details. * * @param other The sparse sets that imposes the order of the entities. */ void respect(const basic_sparse_set &other) { compact(); const auto to = other.end(); auto from = other.begin(); for(size_type pos = packed.size() - 1; pos && from != to; ++from) { if(contains(*from)) { if(*from != packed[pos]) { // basic no-leak guarantee (with invalid state) if swapping throws swap_elements(packed[pos], *from); } --pos; } } } /*! @brief Clears a sparse set. */ void clear() { if(const auto last = end(); free_list == null) { pop(begin(), last); } else { for(auto &&entity: *this) { // tombstone filter on itself if(const auto it = find(entity); it != last) { pop(it, it + 1u); } } } free_list = tombstone; packed.clear(); } /** * @brief Returned value type, if any. * @return Returned value type, if any. */ const type_info &type() const noexcept { return *info; } /*! @brief Forwards variables to derived classes, if any. */ virtual void bind(any) noexcept {} private: sparse_container_type sparse; packed_container_type packed; const type_info *info; entity_type free_list; deletion_policy mode; }; } // namespace entt #endif // #include "storage.hpp" #ifndef ENTT_ENTITY_STORAGE_HPP #define ENTT_ENTITY_STORAGE_HPP #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" // #include "../core/memory.hpp" // #include "../core/type_info.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage_mixin.hpp" #ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP #define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP #include // #include "../config/config.h" // #include "../core/any.hpp" // #include "../signal/sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include // #include "delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_SIGNAL_FWD_HPP #define ENTT_SIGNAL_FWD_HPP #include namespace entt { template class delegate; template> class basic_dispatcher; template> class emitter; class connection; struct scoped_connection; template class sink; template> class sigh; /*! @brief Alias declaration for the most common use case. */ using dispatcher = basic_dispatcher<>; /*! @brief Disambiguation tag for constructors and the like. */ template struct connect_arg_t { /*! @brief Default constructor. */ explicit connect_arg_t() = default; }; /** * @brief Constant of type connect_arg_t used to disambiguate calls. * @tparam Candidate Element to connect (likely a free or member function). */ template inline constexpr connect_arg_t connect_arg{}; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { return std::index_sequence_for{}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void *, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** * @brief Constructs a delegate with a given object or payload, if any. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance Optional valid object that fits the purpose. */ template delegate(connect_arg_t, Type &&...value_or_instance) noexcept { connect(std::forward(value_or_instance)...); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { fn = [](const void *, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type *function, const void *payload = nullptr) noexcept { ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() noexcept { instance = nullptr; fn = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void *data() const noexcept { return instance; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(instance, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } private: const void *instance; function_type *fn; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) -> delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type &&) -> delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; } // namespace entt #endif // #include "fwd.hpp" namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid signal handler type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink>; using alloc_traits = std::allocator_traits; using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink>; /*! @brief Default constructor. */ sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{allocator} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This signal handler. */ sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This signal handler. */ sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } /** * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const noexcept { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for(auto &&call: std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for(auto &&call: calls) { if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); if(func()) { break; } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { if(func(call(args...))) { break; } } else { func(call(args...)); } } } } private: container_type calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void *ref) : disconnect{fn}, signal{ref} {} public: /*! @brief Default constructor. */ connection() : disconnect{}, signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if(disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void *signal; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection &other) : conn{other} {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection &) = delete; /** * @brief Move constructor. * @param other The scoped connection to move from. */ scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection &operator=(const scoped_connection &) = delete; /** * @brief Move assignment operator. * @param other The scoped connection to move from. * @return This scoped connection. */ scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection &operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sink> { using signal_type = sigh; using difference_type = typename signal_type::container_type::difference_type; template static void release(Type value_or_instance, void *signal) { sink{*static_cast(signal)}.disconnect(value_or_instance); } template static void release(void *signal) { sink{*static_cast(signal)}.disconnect(); } auto before(delegate call) { const auto &calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{*this}; other.offset = calls.cend() - it; return other; } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh &ref) noexcept : offset{}, signal{&ref} {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); return before(std::move(call)); } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); return before(std::move(call)); } /** * @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>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ [[nodiscard]] sink before(const void *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.data() == value_or_instance; }); other.offset = calls.cend() - it; } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{*this}; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function (with or without payload), a bound or an * unbound member to a signal. * * The signal isn't responsible for the connected object or the payload, if * any. Users must guarantee that the lifetime of the instance overcomes the * one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. * @return A properly initialized connection object. */ template connection connect(Type &&...value_or_instance) { disconnect(value_or_instance...); delegate call{}; call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** * @brief Disconnects a free function (with or without payload), a bound or * an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. */ template void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @param value_or_instance A valid object that fits the purpose. */ void disconnect(const void *value_or_instance) { if(value_or_instance) { auto &calls = signal->calls; auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type *signal; }; /** * @brief Deduction guide. * * It allows to deduce the signal handler type of a sink directly from the * signal it refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template sink(sigh &) -> sink>; } // namespace entt #endif // #include "fwd.hpp" namespace entt { /** * @brief Mixin type used to add signal support to storage types. * * The function type of a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * This applies to all signals made available. * * @tparam Type The type of the underlying storage. */ template class sigh_storage_mixin final: public Type { using basic_registry_type = basic_registry; using sigh_type = sigh; using basic_iterator = typename Type::basic_iterator; void pop(basic_iterator first, basic_iterator last) override { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); for(; first != last; ++first) { const auto entt = *first; destruction.publish(*owner, entt); const auto it = Type::find(entt); Type::pop(it, it + 1u); } } basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::try_emplace(entt, force_back, value); construction.publish(*owner, entt); return Type::find(entt); } public: /*! @brief Allocator type. */ using allocator_type = typename Type::allocator_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Expected registry type. */ using registry_type = basic_registry_type; /*! @brief Default constructor. */ sigh_storage_mixin() : sigh_storage_mixin{allocator_type{}} {} /** * @brief Constructs an empty storage with a given allocator. * @param allocator The allocator to use. */ explicit sigh_storage_mixin(const allocator_type &allocator) : Type{allocator}, owner{}, construction{allocator}, destruction{allocator}, update{allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ sigh_storage_mixin(sigh_storage_mixin &&other) noexcept : Type{std::move(other)}, owner{other.owner}, construction{std::move(other.construction)}, destruction{std::move(other.destruction)}, update{std::move(other.update)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept : Type{std::move(other), allocator}, owner{other.owner}, construction{std::move(other.construction), allocator}, destruction{std::move(other.destruction), allocator}, update{std::move(other.update), allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { Type::operator=(std::move(other)); owner = other.owner; construction = std::move(other.construction); destruction = std::move(other.destruction); update = std::move(other.update); return *this; } /** * @brief Exchanges the contents with those of a given storage. * @param other Storage to exchange the content with. */ void swap(sigh_storage_mixin &other) { using std::swap; Type::swap(other); swap(owner, other.owner); swap(construction, other.construction); swap(destruction, other.destruction); swap(update, other.update); } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever a new instance is created and assigned to an entity.
* Listeners are invoked after the object has been assigned to the entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_construct() noexcept { return sink{construction}; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is explicitly updated.
* Listeners are invoked after the object has been updated. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_update() noexcept { return sink{update}; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is removed from an entity and thus destroyed.
* Listeners are invoked before the object has been removed from the entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_destroy() noexcept { return sink{destruction}; } /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(const entity_type entt, Args &&...args) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::emplace(entt, std::forward(args)...); construction.publish(*owner, entt); return this->get(entt); } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(const entity_type entt, Func &&...func) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::patch(entt, std::forward(func)...); update.publish(*owner, entt); return this->get(entt); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(It first, It last, Args &&...args) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::insert(first, last, std::forward(args)...); for(auto it = construction.empty() ? last : first; it != last; ++it) { construction.publish(*owner, *it); } } /** * @brief Forwards variables to derived classes, if any. * @param value A variable wrapped in an opaque container. */ void bind(any value) noexcept final { auto *reg = any_cast(&value); owner = reg ? reg : owner; Type::bind(std::move(value)); } private: basic_registry_type *owner; sigh_type construction; sigh_type destruction; sigh_type update; }; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class storage_iterator final { friend storage_iterator; using container_type = std::remove_const_t; using alloc_traits = std::allocator_traits; using comp_traits = component_traits>; using iterator_traits = std::iterator_traits, typename alloc_traits::template rebind_traits::element_type>::const_pointer, typename alloc_traits::template rebind_traits::element_type>::pointer>>; public: using value_type = typename iterator_traits::value_type; using pointer = typename iterator_traits::pointer; using reference = typename iterator_traits::reference; using difference_type = typename iterator_traits::difference_type; using iterator_category = std::random_access_iterator_tag; constexpr storage_iterator() noexcept = default; constexpr storage_iterator(Container *ref, const difference_type idx) noexcept : packed{ref}, offset{idx} {} template, typename = std::enable_if_t> constexpr storage_iterator(const storage_iterator> &other) noexcept : storage_iterator{other.packed, other.offset} {} constexpr storage_iterator &operator++() noexcept { return --offset, *this; } constexpr storage_iterator operator++(int) noexcept { storage_iterator orig = *this; return ++(*this), orig; } constexpr storage_iterator &operator--() noexcept { return ++offset, *this; } constexpr storage_iterator operator--(int) noexcept { storage_iterator orig = *this; return operator--(), orig; } constexpr storage_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } constexpr storage_iterator operator+(const difference_type value) const noexcept { storage_iterator copy = *this; return (copy += value); } constexpr storage_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr storage_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { const auto pos = index() - value; return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; } [[nodiscard]] constexpr pointer operator->() const noexcept { const auto pos = index(); return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } private: Container *packed; difference_type offset; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template [[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template [[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template [[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs < rhs); } template class extended_storage_iterator final { template friend class extended_storage_iterator; public: using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr extended_storage_iterator() : it{} {} constexpr extended_storage_iterator(It base, Other... other) : it{base, other...} {} template && ...) && (std::is_constructible_v && ...)>> constexpr extended_storage_iterator(const extended_storage_iterator &other) : it{other.it} {} constexpr extended_storage_iterator &operator++() noexcept { return ++std::get(it), (++std::get(it), ...), *this; } constexpr extended_storage_iterator operator++(int) noexcept { extended_storage_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {*std::get(it), *std::get(it)...}; } template friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; private: std::tuple it; }; template [[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return std::get<0>(lhs.it) == std::get<0>(rhs.it); } template [[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic storage implementation. * * Internal data structures arrange elements to maximize performance. There are * no guarantees that objects are returned in the insertion order when iterate * a storage. Do not make assumption on the order in any case. * * @warning * Empty types aren't explicitly instantiated. Therefore, many of the functions * normally available for non-empty types will not be available for empty ones. * * @tparam Type Type of objects assigned to the entities. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_storage: public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using underlying_type = basic_sparse_set>; using container_type = std::vector>; using comp_traits = component_traits; static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); [[nodiscard]] auto &element_at(const std::size_t pos) const { return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; } auto assure_at_least(const std::size_t pos) { auto &&container = packed.first(); const auto idx = pos / comp_traits::page_size; if(!(idx < container.size())) { auto curr = container.size(); container.resize(idx + 1u, nullptr); ENTT_TRY { for(const auto last = container.size(); curr < last; ++curr) { container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size); } } ENTT_CATCH { container.resize(curr); ENTT_THROW; } } return container[idx] + fast_mod(pos, comp_traits::page_size); } template auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { const auto it = base_type::try_emplace(entt, force_back); ENTT_TRY { auto elem = assure_at_least(static_cast(it.index())); entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward(args)...); } ENTT_CATCH { base_type::pop(it, it + 1u); ENTT_THROW; } return it; } void shrink_to_size(const std::size_t sz) { for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { if constexpr(comp_traits::in_place_delete) { if(base_type::at(pos) != tombstone) { std::destroy_at(std::addressof(element_at(pos))); } } else { std::destroy_at(std::addressof(element_at(pos))); } } auto &&container = packed.first(); auto page_allocator{packed.second()}; const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size; for(auto pos = from, last = container.size(); pos < last; ++pos) { alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size); } container.resize(from); } private: const void *get_at(const std::size_t pos) const final { return std::addressof(element_at(pos)); } void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final { // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type"); if constexpr(!is_pinned_type_v) { using std::swap; swap(element_at(lhs), element_at(rhs)); } } void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final { // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); if constexpr(!is_pinned_type_v) { auto &elem = element_at(from); entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem)); std::destroy_at(std::addressof(elem)); } } protected: /*! @brief Random access iterator type. */ using basic_iterator = typename underlying_type::basic_iterator; /** * @brief Erases entities from a sparse set. * @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. */ void pop(basic_iterator first, basic_iterator last) override { for(; first != last; ++first) { // cannot use first.index() because it would break with cross iterators auto &elem = element_at(base_type::index(*first)); if constexpr(comp_traits::in_place_delete) { base_type::in_place_pop(first); std::destroy_at(std::addressof(elem)); } else { auto &other = element_at(base_type::size() - 1u); // destroying on exit allows reentrant destructors [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); std::destroy_at(std::addressof(other)); base_type::swap_and_pop(first); } } } /** * @brief Assigns an entity to a storage. * @param entt A valid identifier. * @param value Optional opaque value. * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { if(value) { if constexpr(std::is_copy_constructible_v) { return emplace_element(entt, force_back, *static_cast(value)); } else { return base_type::end(); } } else { if constexpr(std::is_default_constructible_v) { return emplace_element(entt, force_back); } else { return base_type::end(); } } } public: /*! @brief Base type. */ using base_type = underlying_type; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained elements. */ using pointer = typename container_type::pointer; /*! @brief Constant pointer type to contained elements. */ using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; /*! @brief Random access iterator type. */ using iterator = internal::storage_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = std::reverse_iterator; /*! @brief Extended iterable storage proxy. */ using iterable = iterable_adaptor>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() : basic_storage{allocator_type{}} {} /** * @brief Constructs an empty storage with a given allocator. * @param allocator The allocator to use. */ explicit basic_storage(const allocator_type &allocator) : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator}, packed{container_type{allocator}, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage(basic_storage &&other) noexcept : base_type{std::move(other)}, packed{std::move(other.packed)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator}, packed{container_type{std::move(other.packed.first()), allocator}, allocator} { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); } /*! @brief Default destructor. */ ~basic_storage() override { shrink_to_size(0u); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ basic_storage &operator=(basic_storage &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); shrink_to_size(0u); base_type::operator=(std::move(other)); packed.first() = std::move(other.packed.first()); propagate_on_container_move_assignment(packed.second(), other.packed.second()); return *this; } /** * @brief Exchanges the contents with those of a given storage. * @param other Storage to exchange the content with. */ void swap(basic_storage &other) { using std::swap; underlying_type::swap(other); propagate_on_container_swap(packed.second(), other.packed.second()); swap(packed.first(), other.packed.first()); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{packed.second()}; } /** * @brief Increases the capacity of a storage. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) override { if(cap != 0u) { base_type::reserve(cap); assure_at_least(cap - 1u); } } /** * @brief Returns the number of elements that a storage has currently * allocated space for. * @return Capacity of the storage. */ [[nodiscard]] size_type capacity() const noexcept override { return packed.first().size() * comp_traits::page_size; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() override { base_type::shrink_to_fit(); shrink_to_size(base_type::size()); } /** * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ [[nodiscard]] const_pointer raw() const noexcept { return packed.first().data(); } /*! @copydoc raw */ [[nodiscard]] pointer raw() noexcept { return packed.first().data(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the storage is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { const auto pos = static_cast(base_type::size()); return const_iterator{&packed.first(), pos}; } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { const auto pos = static_cast(base_type::size()); return iterator{&packed.first(), pos}; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return const_iterator{&packed.first(), {}}; } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return iterator{&packed.first(), {}}; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first instance of the reversed * internal array. If the storage is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first instance of the reversed internal array. */ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @copydoc rbegin */ [[nodiscard]] reverse_iterator rbegin() noexcept { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last instance * of the reversed internal array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last instance of the * reversed internal array. */ [[nodiscard]] const_reverse_iterator crend() const noexcept { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ [[nodiscard]] const_reverse_iterator rend() const noexcept { return crend(); } /*! @copydoc rend */ [[nodiscard]] reverse_iterator rend() noexcept { return std::make_reverse_iterator(begin()); } /** * @brief Returns the object assigned to an entity. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. * @return The object assigned to the entity. */ [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { return element_at(base_type::index(entt)); } /*! @copydoc get */ [[nodiscard]] value_type &get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Returns the object assigned to an entity as a tuple. * @param entt A valid identifier. * @return The object assigned to the entity as a tuple. */ [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { return std::forward_as_tuple(get(entt)); } /*! @copydoc get_as_tuple */ [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { return std::forward_as_tuple(get(entt)); } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. * @param args Parameters to use to construct an object for the entity. * @return A reference to the newly created object. */ template value_type &emplace(const entity_type entt, Args &&...args) { if constexpr(std::is_aggregate_v) { const auto it = emplace_element(entt, false, Type{std::forward(args)...}); return element_at(static_cast(it.index())); } else { const auto it = emplace_element(entt, false, std::forward(args)...); return element_at(static_cast(it.index())); } } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the updated instance. */ template value_type &patch(const entity_type entt, Func &&...func) { const auto idx = base_type::index(entt); auto &elem = element_at(idx); (std::forward(func)(elem), ...); return elem; } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given instance. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the object to construct. */ template void insert(It first, It last, const value_type &value = {}) { for(; first != last; ++first) { emplace_element(*first, true, value); } } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given range. * * @sa construct * * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of objects. */ template::value_type, value_type>>> void insert(EIt first, EIt last, CIt from) { for(; first != last; ++first, ++from) { emplace_element(*first, true, *from); } } /** * @brief Returns an iterable object to use to _visit_ a storage. * * The iterable object returns a tuple that contains the current entity and * a reference to its component. * * @return An iterable object to use to _visit_ the storage. */ [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; } /*! @copydoc each */ [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; } private: compressed_pair packed; }; /*! @copydoc basic_storage */ template class basic_storage>> : public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using underlying_type = basic_sparse_set>; using comp_traits = component_traits; public: /*! @brief Base type. */ using base_type = underlying_type; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Extended iterable storage proxy. */ using iterable = iterable_adaptor>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() : basic_storage{allocator_type{}} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit basic_storage(const allocator_type &allocator) : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage(basic_storage &&other) noexcept = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ basic_storage &operator=(basic_storage &&other) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{base_type::get_allocator()}; } /** * @brief Returns the object assigned to an entity, that is `void`. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. */ void get([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); } /** * @brief Returns an empty tuple. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. * @return Returns an empty tuple. */ [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); return std::tuple{}; } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. */ template void emplace(const entity_type entt, Args &&...) { base_type::try_emplace(entt, false); } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. */ template void patch([[maybe_unused]] const entity_type entt, Func &&...func) { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); (std::forward(func)(), ...); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of optional arguments. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last, Args &&...) { for(; first != last; ++first) { base_type::try_emplace(*first, true); } } /** * @brief Returns an iterable object to use to _visit_ a storage. * * The iterable object returns a tuple that contains the current entity. * * @return An iterable object to use to _visit_ the storage. */ [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; } /*! @copydoc each */ [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; } }; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class extended_group_iterator; template class extended_group_iterator, get_t> { template auto index_to_element([[maybe_unused]] Type &cpool) const { if constexpr(ignore_as_empty_v) { return std::make_tuple(); } else { return std::forward_as_tuple(cpool.rbegin()[it.index()]); } } public: using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr extended_group_iterator() : it{}, pools{} {} extended_group_iterator(It from, const std::tuple &cpools) : it{from}, pools{cpools} {} extended_group_iterator &operator++() noexcept { return ++it, *this; } extended_group_iterator operator++(int) noexcept { extended_group_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } template friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; private: It it; std::tuple pools; }; template [[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Group. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_group; /** * @brief Non-owning group. * * A non-owning group returns all entities and only the entities that are at * least in the given storage. Moreover, it's guaranteed that the entity list is * tightly packed in memory for fast iterations. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ template class basic_group, get_t, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() noexcept : handler{} {} /** * @brief Constructs a group from a set of storage classes. * @param ref The actual entities to iterate. * @param gpool Storage types to iterate _observed_ by the group. */ basic_group(basic_common_type &ref, Get &...gpool) noexcept : handler{&ref}, pools{&gpool...} {} /** * @brief Returns a const reference to the underlying handler. * @return A const reference to the underlying handler. */ [[nodiscard]] const base_type &handle() const noexcept { return *handler; } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that are part of the group. * @return Number of entities that are part of the group. */ [[nodiscard]] size_type size() const noexcept { return *this ? handler->size() : size_type{}; } /** * @brief Returns the number of elements that a group has currently * allocated space for. * @return Capacity of the group. */ [[nodiscard]] size_type capacity() const noexcept { return *this ? handler->capacity() : size_type{}; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if(*this) { handler->shrink_to_fit(); } } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return !*this || handler->empty(); } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const noexcept { return *this ? handler->begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const noexcept { return *this ? handler->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? handler->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? handler->rend() : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return handler != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && handler->contains(entt); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (std::get>(pools)->get(entt), ...); } else { return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(const auto entt: *this) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } /** * @brief Returns an iterable object to use to _visit_ a group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable each() const noexcept { return iterable{{begin(), pools}, {end(), pools}}; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Type &..., const Type &...); * bool(const Entity, const Entity); * @endcode * * Where `Type` are such that they are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Type Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { if(*this) { if constexpr(sizeof...(Type) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); handler->sort(std::move(compare), std::move(algo), std::forward(args)...); } else { auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { if constexpr(sizeof...(Type) == 1) { return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); } else { return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; handler->sort(std::move(comp), std::move(algo), std::forward(args)...); } } } /** * @brief Sort the shared pool of entities according to the given component. * * Non-owning groups of the same type share with the registry a pool of * entities with its own order that doesn't depend on the order of any pool * of components. Users can order the underlying data structure so that it * respects the order of the pool of the given component. * * @note * The shared pool of entities and thus its order is affected by the changes * to each and every pool that it tracks. Therefore changes to those pools * can quickly ruin the order imposed to the pool of entities shared between * the non-owning groups. * * @tparam Type Type of component to use to impose the order. */ template void sort() const { if(*this) { handler->respect(*std::get>(pools)); } } private: base_type *const handler; const std::tuple pools; }; /** * @brief Owning group. * * Owning groups returns all entities and only the entities that are at * least in the given storage. Moreover: * * * It's guaranteed that the entity list is tightly packed in memory for fast * iterations. * * It's guaranteed that all components in the owned storage are tightly packed * in memory for even faster iterations and to allow direct access. * * They stay true to the order of the owned storage and all instances have the * same order in memory. * * The more types of storage are owned, the faster it is to iterate a group. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Owned Types of storage _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ template class basic_group, get_t, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() noexcept : length{} {} /** * @brief Constructs a group from a set of storage classes. * @param extent The actual number of entities to iterate. * @param opool Storage types to iterate _owned_ by the group. * @param gpool Storage types to iterate _observed_ by the group. */ basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept : pools{&opool..., &gpool...}, length{&extent} {} /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that that are part of the group. * @return Number of entities that that are part of the group. */ [[nodiscard]] size_type size() const noexcept { return *this ? *length : size_type{}; } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return !*this || !*length; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const noexcept { return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const noexcept { return *this ? std::get<0>(pools)->base_type::end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; return it != end() && it >= begin() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return length != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (std::get>(pools)->get(entt), ...); } else { return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(auto args: each()) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, args); } else { std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); } } } /** * @brief Returns an iterable object to use to _visit_ a group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable each() const noexcept { return {{begin(), pools}, {end(), pools}}; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Type &, const Type &); * bool(const Entity, const Entity); * @endcode * * Where `Type` are either owned types or not but still such that they are * iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Type Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { if constexpr(sizeof...(Type) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); } else { auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { if constexpr(sizeof...(Type) == 1) { return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); } else { return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); } std::apply([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_elements(other->data()[pos], entt), ...); } }, pools); } private: const std::tuple pools; const size_type *const length; }; } // namespace entt #endif // #include "entity/handle.hpp" #ifndef ENTT_ENTITY_HANDLE_HPP #define ENTT_ENTITY_HANDLE_HPP #include #include #include #include // #include "../core/iterator.hpp" // #include "../core/type_traits.hpp" // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class handle_storage_iterator final { template friend class handle_storage_iterator; using underlying_type = std::remove_reference_t; using entity_type = typename underlying_type::entity_type; public: using value_type = typename std::iterator_traits::value_type; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr handle_storage_iterator() noexcept : entt{null}, it{}, last{} {} constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept : entt{value}, it{from}, last{to} { while(it != last && !it->second.contains(entt)) { ++it; } } constexpr handle_storage_iterator &operator++() noexcept { while(++it != last && !it->second.contains(entt)) {} return *this; } constexpr handle_storage_iterator operator++(int) noexcept { handle_storage_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr reference operator*() const noexcept { return *it; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } template friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; private: entity_type entt; It it; It last; }; template [[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Non-owning handle to an entity. * * Tiny wrapper around a registry and an entity. * * @tparam Registry Basic registry type. * @tparam Scope Types to which to restrict the scope of a handle. */ template struct basic_handle { /*! @brief Type of registry accepted by the handle. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /*! @brief Underlying version type. */ using version_type = typename registry_type::version_type; /*! @brief Unsigned integer type. */ using size_type = typename registry_type::size_type; /*! @brief Constructs an invalid handle. */ basic_handle() noexcept : reg{}, entt{null} {} /** * @brief Constructs a handle from a given registry and entity. * @param ref An instance of the registry class. * @param value A valid identifier. */ basic_handle(registry_type &ref, entity_type value) noexcept : reg{&ref}, entt{value} {} /** * @brief Returns an iterable object to use to _visit_ a handle. * * The iterable object returns a pair that contains the name and a reference * to the current storage.
* Returned storage are those that contain the entity associated with the * handle. * * @return An iterable object to use to _visit_ the handle. */ [[nodiscard]] auto storage() const noexcept { auto iterable = reg->storage(); using iterator_type = internal::handle_storage_iterator; return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; } /** * @brief Constructs a const handle from a non-const one. * @tparam Other A valid entity type (see entt_traits for more details). * @tparam Args Scope of the handle to construct. * @return A const handle referring to the same registry and the same * entity. */ template operator basic_handle() const noexcept { static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); return reg ? basic_handle{*reg, entt} : basic_handle{}; } /** * @brief Converts a handle to its underlying entity. * @return The contained identifier. */ [[nodiscard]] operator entity_type() const noexcept { return entity(); } /** * @brief Checks if a handle refers to non-null registry pointer and entity. * @return True if the handle refers to non-null registry and entity, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return reg && reg->valid(entt); } /** * @brief Checks if a handle refers to a valid entity or not. * @return True if the handle refers to a valid entity, false otherwise. */ [[nodiscard]] bool valid() const { return reg->valid(entt); } /** * @brief Returns a pointer to the underlying registry, if any. * @return A pointer to the underlying registry, if any. */ [[nodiscard]] registry_type *registry() const noexcept { return reg; } /** * @brief Returns the entity associated with a handle. * @return The entity associated with the handle. */ [[nodiscard]] entity_type entity() const noexcept { return entt; } /*! @brief Destroys the entity associated with a handle. */ void destroy() { reg->destroy(entt); } /** * @brief Destroys the entity associated with a handle. * @param version A desired version upon destruction. */ void destroy(const version_type version) { reg->destroy(entt, version); } /** * @brief Assigns the given component to a handle. * @tparam Component Type of component to create. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace(Args &&...args) const { static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace(entt, std::forward(args)...); } /** * @brief Assigns or replaces the given component for a handle. * @tparam Component Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace_or_replace(Args &&...args) const { static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template emplace_or_replace(entt, std::forward(args)...); } /** * @brief Patches the given component for a handle. * @tparam Component Type of component to patch. * @tparam Func Types of the function objects to invoke. * @param func Valid function objects. * @return A reference to the patched component. */ template decltype(auto) patch(Func &&...func) const { static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template patch(entt, std::forward(func)...); } /** * @brief Replaces the given component for a handle. * @tparam Component Type of component to replace. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ template decltype(auto) replace(Args &&...args) const { static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template replace(entt, std::forward(args)...); } /** * @brief Removes the given components from a handle. * @tparam Component Types of components to remove. * @return The number of components actually removed. */ template size_type remove() const { static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template remove(entt); } /** * @brief Erases the given components from a handle. * @tparam Component Types of components to erase. */ template void erase() const { static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); reg->template erase(entt); } /** * @brief Checks if a handle has all the given components. * @tparam Component Components for which to perform the check. * @return True if the handle has all the components, false otherwise. */ template [[nodiscard]] decltype(auto) all_of() const { return reg->template all_of(entt); } /** * @brief Checks if a handle has at least one of the given components. * @tparam Component Components for which to perform the check. * @return True if the handle has at least one of the given components, * false otherwise. */ template [[nodiscard]] decltype(auto) any_of() const { return reg->template any_of(entt); } /** * @brief Returns references to the given components for a handle. * @tparam Component Types of components to get. * @return References to the components owned by the handle. */ template [[nodiscard]] decltype(auto) get() const { static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template get(entt); } /** * @brief Returns a reference to the given component for a handle. * @tparam Component Type of component to get. * @tparam Args Types of arguments to use to construct the component. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the handle. */ template [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); return reg->template get_or_emplace(entt, std::forward(args)...); } /** * @brief Returns pointers to the given components for a handle. * @tparam Component Types of components to get. * @return Pointers to the components owned by the handle. */ template [[nodiscard]] auto try_get() const { static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); return reg->template try_get(entt); } /** * @brief Checks if a handle has components assigned. * @return True if the handle has no components assigned, false otherwise. */ [[nodiscard]] bool orphan() const { return reg->orphan(entt); } private: registry_type *reg; entity_type entt; }; /** * @brief Compares two handles. * @tparam Args Scope of the first handle. * @tparam Other Scope of the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if both handles refer to the same registry and the same * entity, false otherwise. */ template [[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); } /** * @brief Compares two handles. * @tparam Args Scope of the first handle. * @tparam Other Scope of the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return False if both handles refer to the same registry and the same * entity, true otherwise. */ template [[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { return !(lhs == rhs); } } // namespace entt #endif // #include "entity/helper.hpp" #ifndef ENTT_ENTITY_HELPER_HPP #define ENTT_ENTITY_HELPER_HPP #include #include // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { return std::index_sequence_for{}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void *, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** * @brief Constructs a delegate with a given object or payload, if any. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance Optional valid object that fits the purpose. */ template delegate(connect_arg_t, Type &&...value_or_instance) noexcept { connect(std::forward(value_or_instance)...); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { fn = [](const void *, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type *function, const void *payload = nullptr) noexcept { ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() noexcept { instance = nullptr; fn = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void *data() const noexcept { return instance; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(instance, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } private: const void *instance; function_type *fn; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) -> delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type &&) -> delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; } // namespace entt #endif // #include "component.hpp" // #include "fwd.hpp" // #include "group.hpp" #ifndef ENTT_ENTITY_GROUP_HPP #define ENTT_ENTITY_GROUP_HPP #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class extended_group_iterator; template class extended_group_iterator, get_t> { template auto index_to_element([[maybe_unused]] Type &cpool) const { if constexpr(ignore_as_empty_v) { return std::make_tuple(); } else { return std::forward_as_tuple(cpool.rbegin()[it.index()]); } } public: using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr extended_group_iterator() : it{}, pools{} {} extended_group_iterator(It from, const std::tuple &cpools) : it{from}, pools{cpools} {} extended_group_iterator &operator++() noexcept { return ++it, *this; } extended_group_iterator operator++(int) noexcept { extended_group_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } template friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; private: It it; std::tuple pools; }; template [[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Group. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_group; /** * @brief Non-owning group. * * A non-owning group returns all entities and only the entities that are at * least in the given storage. Moreover, it's guaranteed that the entity list is * tightly packed in memory for fast iterations. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ template class basic_group, get_t, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() noexcept : handler{} {} /** * @brief Constructs a group from a set of storage classes. * @param ref The actual entities to iterate. * @param gpool Storage types to iterate _observed_ by the group. */ basic_group(basic_common_type &ref, Get &...gpool) noexcept : handler{&ref}, pools{&gpool...} {} /** * @brief Returns a const reference to the underlying handler. * @return A const reference to the underlying handler. */ [[nodiscard]] const base_type &handle() const noexcept { return *handler; } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that are part of the group. * @return Number of entities that are part of the group. */ [[nodiscard]] size_type size() const noexcept { return *this ? handler->size() : size_type{}; } /** * @brief Returns the number of elements that a group has currently * allocated space for. * @return Capacity of the group. */ [[nodiscard]] size_type capacity() const noexcept { return *this ? handler->capacity() : size_type{}; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() { if(*this) { handler->shrink_to_fit(); } } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return !*this || handler->empty(); } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const noexcept { return *this ? handler->begin() : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const noexcept { return *this ? handler->end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? handler->rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? handler->rend() : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { const auto it = *this ? handler->find(entt) : iterator{}; return it != end() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return handler != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && handler->contains(entt); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (std::get>(pools)->get(entt), ...); } else { return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(const auto entt: *this) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); } else { std::apply(func, get(entt)); } } } /** * @brief Returns an iterable object to use to _visit_ a group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable each() const noexcept { return iterable{{begin(), pools}, {end(), pools}}; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Type &..., const Type &...); * bool(const Entity, const Entity); * @endcode * * Where `Type` are such that they are iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Type Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { if(*this) { if constexpr(sizeof...(Type) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); handler->sort(std::move(compare), std::move(algo), std::forward(args)...); } else { auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { if constexpr(sizeof...(Type) == 1) { return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); } else { return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; handler->sort(std::move(comp), std::move(algo), std::forward(args)...); } } } /** * @brief Sort the shared pool of entities according to the given component. * * Non-owning groups of the same type share with the registry a pool of * entities with its own order that doesn't depend on the order of any pool * of components. Users can order the underlying data structure so that it * respects the order of the pool of the given component. * * @note * The shared pool of entities and thus its order is affected by the changes * to each and every pool that it tracks. Therefore changes to those pools * can quickly ruin the order imposed to the pool of entities shared between * the non-owning groups. * * @tparam Type Type of component to use to impose the order. */ template void sort() const { if(*this) { handler->respect(*std::get>(pools)); } } private: base_type *const handler; const std::tuple pools; }; /** * @brief Owning group. * * Owning groups returns all entities and only the entities that are at * least in the given storage. Moreover: * * * It's guaranteed that the entity list is tightly packed in memory for fast * iterations. * * It's guaranteed that all components in the owned storage are tightly packed * in memory for even faster iterations and to allow direct access. * * They stay true to the order of the owned storage and all instances have the * same order in memory. * * The more types of storage are owned, the faster it is to iterate a group. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the group in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Owned Types of storage _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. */ template class basic_group, get_t, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template static constexpr std::size_t index_of = type_list_index_v, type_list>; public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable group type. */ using iterable = iterable_adaptor, get_t>>; /*! @brief Default constructor to use to create empty, invalid groups. */ basic_group() noexcept : length{} {} /** * @brief Constructs a group from a set of storage classes. * @param extent The actual number of entities to iterate. * @param opool Storage types to iterate _owned_ by the group. * @param gpool Storage types to iterate _observed_ by the group. */ basic_group(const std::size_t &extent, Owned &...opool, Get &...gpool) noexcept : pools{&opool..., &gpool...}, length{&extent} {} /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that that are part of the group. * @return Number of entities that that are part of the group. */ [[nodiscard]] size_type size() const noexcept { return *this ? *length : size_type{}; } /** * @brief Checks whether a group is empty. * @return True if the group is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return !*this || !*length; } /** * @brief Returns an iterator to the first entity of the group. * * The returned iterator points to the first entity of the group. If the * group is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the group. */ [[nodiscard]] iterator begin() const noexcept { return *this ? (std::get<0>(pools)->base_type::end() - *length) : iterator{}; } /** * @brief Returns an iterator that is past the last entity of the group. * * The returned iterator points to the entity following the last entity of * the group. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * group. */ [[nodiscard]] iterator end() const noexcept { return *this ? std::get<0>(pools)->base_type::end() : iterator{}; } /** * @brief Returns an iterator to the first entity of the reversed group. * * The returned iterator points to the first entity of the reversed group. * If the group is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed group. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return *this ? std::get<0>(pools)->base_type::rbegin() : reverse_iterator{}; } /** * @brief Returns an iterator that is past the last entity of the reversed * group. * * The returned iterator points to the entity following the last entity of * the reversed group. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed group. */ [[nodiscard]] reverse_iterator rend() const noexcept { return *this ? (std::get<0>(pools)->base_type::rbegin() + *length) : reverse_iterator{}; } /** * @brief Returns the first entity of the group, if any. * @return The first entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the group, if any. * @return The last entity of the group if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { const auto it = rbegin(); return it != rend() ? *it : null; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { const auto it = *this ? std::get<0>(pools)->find(entt) : iterator{}; return it != end() && it >= begin() && *it == entt ? it : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Checks if a group is properly initialized. * @return True if the group is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return length != nullptr; } /** * @brief Checks if a group contains an entity. * @param entt A valid identifier. * @return True if the group contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return *this && std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < (*length)); } /** * @brief Returns the components assigned to the given entity. * * Prefer this function instead of `registry::get` during iterations. It has * far better performance than its counterpart. * * @warning * Attempting to use an invalid component type results in a compilation * error. Attempting to use an entity that doesn't belong to the group * results in undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (std::get>(pools)->get(entt), ...); } else { return std::tuple_cat(std::get>(pools)->get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(auto args: each()) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, args); } else { std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); } } } /** * @brief Returns an iterable object to use to _visit_ a group. * * The iterable object returns tuples that contain the current entity and a * set of references to its non-empty components. The _constness_ of the * components is as requested. * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @return An iterable object to use to _visit_ the group. */ [[nodiscard]] iterable each() const noexcept { return {{begin(), pools}, {end(), pools}}; } /** * @brief Sort a group according to the given comparison function. * * Sort the group so that iterating it with a couple of iterators returns * entities and components in the expected order. See `begin` and `end` for * more details. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to one of the following: * * @code{.cpp} * bool(std::tuple, std::tuple); * bool(const Type &, const Type &); * bool(const Entity, const Entity); * @endcode * * Where `Type` are either owned types or not but still such that they are * iterated by the group.
* Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Type Optional types of components to compare. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { if constexpr(sizeof...(Type) == 0) { static_assert(std::is_invocable_v, "Invalid comparison function"); std::get<0>(pools)->sort_n(*length, std::move(compare), std::move(algo), std::forward(args)...); } else { auto comp = [this, &compare](const entity_type lhs, const entity_type rhs) { if constexpr(sizeof...(Type) == 1) { return compare((std::get>(pools)->get(lhs), ...), (std::get>(pools)->get(rhs), ...)); } else { return compare(std::forward_as_tuple(std::get>(pools)->get(lhs)...), std::forward_as_tuple(std::get>(pools)->get(rhs)...)); } }; std::get<0>(pools)->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); } std::apply([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_elements(other->data()[pos], entt), ...); } }, pools); } private: const std::tuple pools; const size_type *const length; }; } // namespace entt #endif // #include "view.hpp" #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class view_iterator final { using iterator_type = typename Type::const_iterator; [[nodiscard]] bool valid() const noexcept { return ((Get != 0u) || (*it != tombstone)) && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } public: using value_type = typename iterator_type::value_type; using pointer = typename iterator_type::pointer; using reference = typename iterator_type::reference; using difference_type = typename iterator_type::difference_type; using iterator_category = std::forward_iterator_tag; constexpr view_iterator() noexcept : it{}, last{}, pools{}, filter{} {} view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) noexcept : it{curr}, last{to}, pools{all_of}, filter{none_of} { while(it != last && !valid()) { ++it; } } view_iterator &operator++() noexcept { while(++it != last && !valid()) {} return *this; } view_iterator operator++(int) noexcept { view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] pointer operator->() const noexcept { return &*it; } [[nodiscard]] reference operator*() const noexcept { return *operator->(); } template friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; private: iterator_type it; iterator_type last; std::array pools; std::array filter; }; template [[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { return !(lhs == rhs); } template struct extended_view_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr extended_view_iterator() : it{}, pools{} {} extended_view_iterator(It from, std::tuple storage) : it{from}, pools{storage} {} extended_view_iterator &operator++() noexcept { return ++it, *this; } extended_view_iterator operator++(int) noexcept { extended_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } template friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; private: It it; std::tuple pools; }; template [[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_view; /** * @brief Multi component view. * * Multi component views iterate over those entities that are at least in the * given storage. During initialization, a multi component view looks at the * number of entities available for each component and uses the smallest set in * order to get a performance boost when iterating. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. */ template class basic_view, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template friend class basic_view; template static constexpr std::size_t index_of = type_list_index_v, type_list>; [[nodiscard]] auto opaque_check_set() const noexcept { std::array other{}; std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); return other; } [[nodiscard]] auto filter_as_array() const noexcept { return std::apply([](const auto *...curr) { return std::array{curr...}; }, filter); } template [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { if constexpr(Curr == Other) { return std::forward_as_tuple(std::get(curr)...); } else { return storage().get_as_tuple(std::get<0>(curr)); } } [[nodiscard]] auto reject(const underlying_type entt) const noexcept { return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); } template void each(Func &func, std::index_sequence) const { for(const auto curr: storage().each()) { if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage().contains(entt)) && ...) && !reject(entt)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); } else { std::apply(func, std::tuple_cat(dispatch_get(curr)...)); } } } } template void pick_and_each(Func &func, std::index_sequence seq) const { ((&storage() == view ? each(func, seq) : void()), ...); } public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable = iterable_adaptor>; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{}, view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param exclude The storage for the types used to filter the view. */ basic_view(Get &...value, Exclude &...exclude) noexcept : pools{&value...}, filter{&exclude...}, view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param excl The storage for the types used to filter the view. */ basic_view(std::tuple value, std::tuple excl = {}) noexcept : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, excl)}, view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Type Type of component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template [[nodiscard]] basic_view use() const noexcept { return use>(); } /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Index Index of the component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template [[nodiscard]] basic_view use() const noexcept { basic_view other{*this}; other.view = &storage(); return other; } /** * @brief Updates the internal leading view if required. * @return A newly created and internally optimized view. */ [[nodiscard]] basic_view refresh() const noexcept { return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); } /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return *view; } /** * @brief Returns the storage for a given component type. * @tparam Comp Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const noexcept { return view->size(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { auto it = view->rbegin(); for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} return it == view->rend() ? null : *it; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); } /** * @brief Returns the components assigned to the given entity. * @param entt A valid identifier. * @return The components assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return view != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (storage>().get(entt), ...); } else { return std::tuple_cat(storage>().get_as_tuple(entt)...); } } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam First Index of a component to get. * @tparam Other Indexes of other components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Other) == 0) { return storage().get(entt); } else { return std::tuple_cat(storage().get_as_tuple(entt), storage().get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { pick_and_each(func, std::index_sequence_for{}); } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a set of references to its non-empty components. The _constness_ of the * components is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam OGet Component list of the view to combine with. * @tparam OExclude Filter list of the view to combine with. * @param other The view to combine with. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { return std::make_from_tuple, exclude_t>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); } private: std::tuple pools; std::tuple filter; const base_type *view; }; /** * @brief Single component view specialization. * * Single component views are specialized in order to get a boost in terms of * performance. This kind of views can access the underlying data structure * directly and avoid superfluous checks. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Type of storage iterated by the view. */ template class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { template friend class basic_view; public: /*! @brief Underlying entity identifier. */ using entity_type = typename Get::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = typename Get::base_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable = decltype(std::declval().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(Get &ref) noexcept : pools{&ref}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(std::tuple ref, std::tuple<> = {}) noexcept : pools{&std::get<0>(ref)}, filter{} {} /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return storage(); } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); return storage<0>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that have the given component. * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const noexcept { return handle().size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handle().empty(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { return handle().begin(); } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return handle().end(); } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return handle().rbegin(); } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { return handle().rend(); } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { return empty() ? null : *begin(); } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { return empty() ? null : *rbegin(); } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? handle().find(entt) : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Returns the component assigned to the given entity. * @param entt A valid identifier. * @return The component assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return storage().get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return std::get<0>(pools) != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return handle().contains(entt); } /** * @brief Returns the component assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Type or index of the component to get. * @param entt A valid identifier. * @return The component assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return storage().get_as_tuple(entt); } else { static_assert((std::is_same_v, typename Get::value_type> && ...), "Invalid component type"); return storage().get(entt); } } /*! @copydoc get */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { return storage().get(entt); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a reference to the component if it's a non-empty one. * The _constness_ of the component is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &); * void(typename Type &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if constexpr(is_applicable_v) { for(const auto pack: each()) { std::apply(func, pack); } } else if constexpr(ignore_as_empty_v) { for(size_type pos{}, last = size(); pos < last; ++pos) { func(); } } else { for(auto &&component: storage()) { func(component); } } } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { return storage().each(); } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam OGet Component list of the view to combine with. * @tparam OExclude Filter list of the view to combine with. * @param other The view to combine with. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { return std::make_from_tuple, exclude_t>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); } private: std::tuple pools; std::tuple<> filter; }; /** * @brief Deduction guide. * @tparam Type Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Type &...storage) -> basic_view, exclude_t<>>; /** * @brief Deduction guide. * @tparam Get Types of components iterated by the view. * @tparam Exclude Types of components used to filter the view. */ template basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; } // namespace entt #endif namespace entt { /** * @brief Converts a registry to a view. * @tparam Registry Basic registry type. */ template class as_view { template auto dispatch(get_t, exclude_t) const { return reg.template view...>(exclude_t...>{}); } public: /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_view(registry_type &source) noexcept : reg{source} {} /** * @brief Conversion function from a registry to a view. * @tparam Get Type of storage used to construct the view. * @tparam Exclude Types of storage used to filter the view. * @return A newly created view. */ template operator basic_view() const { return dispatch(Get{}, Exclude{}); } private: registry_type ® }; /** * @brief Converts a registry to a group. * @tparam Registry Basic registry type. */ template class as_group { template auto dispatch(owned_t, get_t, exclude_t) const { if constexpr(std::is_const_v) { return reg.template group_if_exists(get_t{}, exclude_t{}); } else { return reg.template group...>(get_t...>{}, exclude_t...>{}); } } public: /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_group(registry_type &source) noexcept : reg{source} {} /** * @brief Conversion function from a registry to a group. * @tparam Owned Types of _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. * @return A newly created group. */ template operator basic_group() const { return dispatch(Owned{}, Get{}, Exclude{}); } private: registry_type ® }; /** * @brief Helper to create a listener that directly invokes a member function. * @tparam Member Member function to invoke on a component of the given type. * @tparam Registry Basic registry type. * @param reg A registry that contains the given entity and its components. * @param entt Entity from which to get the component. */ template>> void invoke(Registry ®, const typename Registry::entity_type entt) { static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); delegate func; func.template connect(reg.template get>(entt)); func(reg, entt); } /** * @brief Returns the entity associated with a given component. * * @warning * Currently, this function only works correctly with the default pool as it * makes assumptions about how the components are laid out. * * @tparam Registry Basic registry type. * @tparam Component Type of component. * @param reg A registry that contains the given entity and its components. * @param instance A valid component instance. * @return The entity associated with the given component. */ template typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { const auto &storage = reg.template storage(); const typename Registry::base_type &base = storage; const auto *addr = std::addressof(instance); for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits::page_size) { if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(component_traits::page_size)) { return *(it + dist); } } return null; } } // namespace entt #endif // #include "entity/observer.hpp" #ifndef ENTT_ENTITY_OBSERVER_HPP #define ENTT_ENTITY_OBSERVER_HPP #include #include #include #include #include // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" // #include "fwd.hpp" // #include "storage.hpp" namespace entt { /*! @brief Grouping matcher. */ template struct matcher {}; /** * @brief Collector. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template struct basic_collector; /** * @brief Collector. * * A collector contains a set of rules (literally, matchers) to use to track * entities.
* Its main purpose is to generate a descriptor that allows an observer to know * how to connect to a registry. */ template<> struct basic_collector<> { /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) noexcept { return basic_collector, type_list<>, type_list, AllOf...>>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() noexcept { return basic_collector, type_list<>, AnyOf>>{}; } }; /** * @brief Collector. * @copydetails basic_collector<> * @tparam Reject Untracked types used to filter out entities. * @tparam Require Untracked types required by the matcher. * @tparam Rule Specific details of the current matcher. * @tparam Other Other matchers. */ template struct basic_collector, type_list, Rule...>, Other...> { /*! @brief Current matcher. */ using current_type = matcher, type_list, Rule...>; /** * @brief Adds a grouping matcher to the collector. * @tparam AllOf Types of components tracked by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto group(exclude_t = {}) noexcept { return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; } /** * @brief Adds an observing matcher to the collector. * @tparam AnyOf Type of component for which changes should be detected. * @return The updated collector. */ template static constexpr auto update() noexcept { return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; } /** * @brief Updates the filter of the last added matcher. * @tparam AllOf Types of components required by the matcher. * @tparam NoneOf Types of components used to filter out entities. * @return The updated collector. */ template static constexpr auto where(exclude_t = {}) noexcept { using extended_type = matcher, type_list, Rule...>; return basic_collector{}; } }; /*! @brief Variable template used to ease the definition of collectors. */ inline constexpr basic_collector<> collector{}; /** * @brief Observer. * * An observer returns all the entities and only the entities that fit the * requirements of at least one matcher. Moreover, it's guaranteed that the * entity list is tightly packed in memory for fast iterations.
* In general, observers don't stay true to the order of any set of components. * * Observers work mainly with two types of matchers, provided through a * collector: * * * Observing matcher: an observer will return at least all the living entities * for which one or more of the given components have been updated and not yet * destroyed. * * Grouping matcher: an observer will return at least all the living entities * that would have entered the given group if it existed and that would have * not yet left it. * * If an entity respects the requirements of multiple matchers, it will be * returned once and only once by the observer in any case. * * Matchers support also filtering by means of a _where_ clause that accepts * both a list of types and an exclusion list.
* Whenever a matcher finds that an entity matches its requirements, the * condition of the filter is verified before to register the entity itself. * Moreover, a registered entity isn't returned by the observer if the condition * set by the filter is broken in the meantime. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @warning * Lifetime of an observer doesn't necessarily have to overcome that of the * registry to which it is connected. However, the observer must be disconnected * from the registry before being destroyed to avoid crashes due to dangling * pointers. * * @tparam Registry Basic registry type. */ template class basic_observer: private basic_storage { using base_type = basic_storage; template struct matcher_handler; template struct matcher_handler, type_list, AnyOf>> { template static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { if(reg.template all_of(entt) && !reg.template any_of(entt)) { if(!obs.contains(entt)) { obs.emplace(entt); } obs.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { obs.erase(entt); } } template static void connect(basic_observer &obs, Registry ®) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); reg.template on_update().template connect<&maybe_valid_if>(obs); reg.template on_destroy().template connect<&discard_if>(obs); } static void disconnect(basic_observer &obs, Registry ®) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); reg.template on_update().disconnect(obs); reg.template on_destroy().disconnect(obs); } }; template struct matcher_handler, type_list, type_list, AllOf...>> { template static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { auto condition = [®, entt]() { if constexpr(sizeof...(Ignore) == 0) { return reg.template all_of(entt) && !reg.template any_of(entt); } else { return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); } }; if(condition()) { if(!obs.contains(entt)) { obs.emplace(entt); } obs.get(entt) |= (1 << Index); } } template static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { obs.erase(entt); } } template static void connect(basic_observer &obs, Registry ®) { (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); (reg.template on_destroy().template connect<&discard_if>(obs), ...); (reg.template on_construct().template connect<&discard_if>(obs), ...); } static void disconnect(basic_observer &obs, Registry ®) { (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_destroy().disconnect(obs), ...); (reg.template on_construct().disconnect(obs), ...); } }; template static void disconnect(Registry ®, basic_observer &obs) { (matcher_handler::disconnect(obs, reg), ...); } template void connect(Registry ®, std::index_sequence) { static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); (matcher_handler::template connect(*this, reg), ...); release.template connect<&basic_observer::disconnect>(reg); } public: /*! Basic registry type. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Random access iterator type. */ using iterator = typename registry_type::base_type::iterator; /*! @brief Default constructor. */ basic_observer() : release{} {} /*! @brief Default copy constructor, deleted on purpose. */ basic_observer(const basic_observer &) = delete; /*! @brief Default move constructor, deleted on purpose. */ basic_observer(basic_observer &&) = delete; /** * @brief Creates an observer and connects it to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template basic_observer(registry_type ®, basic_collector) : basic_observer{} { connect(reg, std::index_sequence_for{}); } /*! @brief Default destructor. */ ~basic_observer() = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This observer. */ basic_observer &operator=(const basic_observer &) = delete; /** * @brief Default move assignment operator, deleted on purpose. * @return This observer. */ basic_observer &operator=(basic_observer &&) = delete; /** * @brief Connects an observer to a given registry. * @tparam Matcher Types of matchers to use to initialize the observer. * @param reg A valid reference to a registry. */ template void connect(registry_type ®, basic_collector) { disconnect(); connect(reg, std::index_sequence_for{}); base_type::clear(); } /*! @brief Disconnects an observer from the registry it keeps track of. */ void disconnect() { if(release) { release(*this); release.reset(); } } /** * @brief Returns the number of elements in an observer. * @return Number of elements. */ [[nodiscard]] size_type size() const noexcept { return base_type::size(); } /** * @brief Checks whether an observer is empty. * @return True if the observer is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return base_type::empty(); } /** * @brief Direct access to the list of entities of the observer. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the container is empty. * * @note * Entities are in the reverse order as returned by the `begin`/`end` * iterators. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type *data() const noexcept { return base_type::data(); } /** * @brief Returns an iterator to the first entity of the observer. * * The returned iterator points to the first entity of the observer. If the * container is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the observer. */ [[nodiscard]] iterator begin() const noexcept { return base_type::base_type::begin(); } /** * @brief Returns an iterator that is past the last entity of the observer. * * The returned iterator points to the entity following the last entity of * the observer. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the * observer. */ [[nodiscard]] iterator end() const noexcept { return base_type::base_type::end(); } /*! @brief Clears the underlying container. */ void clear() noexcept { base_type::clear(); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity.
* The signature of the function must be equivalent to the following form: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(const auto entity: *this) { func(entity); } } /** * @brief Iterates entities and applies the given function object to them, * then clears the observer. * * @sa each * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) { std::as_const(*this).each(std::move(func)); clear(); } private: delegate release; }; } // namespace entt #endif // #include "entity/organizer.hpp" #ifndef ENTT_ENTITY_ORGANIZER_HPP #define ENTT_ENTITY_ORGANIZER_HPP #include #include #include #include // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "../graph/adjacency_matrix.hpp" #ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP #define ENTT_GRAPH_ADJACENCY_MATRIX_HPP #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_GRAPH_FWD_HPP #define ENTT_GRAPH_FWD_HPP #include #include // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /*! @brief Undirected graph category tag. */ struct directed_tag {}; /*! @brief Directed graph category tag. */ struct undirected_tag: directed_tag {}; template> class adjacency_matrix; template> class basic_flow; /*! @brief Alias declaration for the most common use case. */ using flow = basic_flow<>; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class edge_iterator { using size_type = std::size_t; public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr edge_iterator() noexcept : it{}, vert{}, pos{}, last{}, offset{} {} constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept : it{std::move(base)}, vert{vertices}, pos{from}, last{to}, offset{step} { for(; pos != last && !it[pos]; pos += offset) {} } constexpr edge_iterator &operator++() noexcept { for(pos += offset; pos != last && !it[pos]; pos += offset) {} return *this; } constexpr edge_iterator operator++(int) noexcept { edge_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::make_pair(pos / vert, pos % vert); } template friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; private: It it; size_type vert; size_type pos; size_type last; size_type offset{}; }; template [[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return lhs.pos == rhs.pos; } template [[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic implementation of a directed adjacency matrix. * @tparam Category Either a directed or undirected category tag. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class adjacency_matrix { using alloc_traits = std::allocator_traits; static_assert(std::is_base_of_v, "Invalid graph category"); static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Vertex type. */ using vertex_type = size_type; /*! @brief Edge type. */ using edge_type = std::pair; /*! @brief Vertex iterator type. */ using vertex_iterator = iota_iterator; /*! @brief Edge iterator type. */ using edge_iterator = internal::edge_iterator; /*! @brief Out edge iterator type. */ using out_edge_iterator = edge_iterator; /*! @brief In edge iterator type. */ using in_edge_iterator = edge_iterator; /*! @brief Graph category tag. */ using graph_category = Category; /*! @brief Default constructor. */ adjacency_matrix() noexcept(noexcept(allocator_type{})) : adjacency_matrix{0u} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit adjacency_matrix(const allocator_type &allocator) noexcept : adjacency_matrix{0u, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied number of vertices. * @param vertices Number of vertices. * @param allocator The allocator to use. */ adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) : matrix{vertices * vertices, allocator}, vert{vertices} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ adjacency_matrix(const adjacency_matrix &other) : adjacency_matrix{other, other.get_allocator()} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) : matrix{other.matrix, allocator}, vert{other.vert} {} /** * @brief Move constructor. * @param other The instance to move from. */ adjacency_matrix(adjacency_matrix &&other) noexcept : adjacency_matrix{std::move(other), other.get_allocator()} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) : matrix{std::move(other.matrix), allocator}, vert{std::exchange(other.vert, 0u)} {} /** * @brief Default copy assignment operator. * @param other The instance to copy from. * @return This container. */ adjacency_matrix &operator=(const adjacency_matrix &other) { matrix = other.matrix; vert = other.vert; return *this; } /** * @brief Default move assignment operator. * @param other The instance to move from. * @return This container. */ adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { matrix = std::move(other.matrix); vert = std::exchange(other.vert, 0u); return *this; } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return matrix.get_allocator(); } /*! @brief Clears the adjacency matrix. */ void clear() noexcept { matrix.clear(); vert = {}; } /** * @brief Exchanges the contents with those of a given adjacency matrix. * @param other Adjacency matrix to exchange the content with. */ void swap(adjacency_matrix &other) { using std::swap; swap(matrix, other.matrix); swap(vert, other.vert); } /** * @brief Returns the number of vertices. * @return The number of vertices. */ [[nodiscard]] size_type size() const noexcept { return vert; } /** * @brief Returns an iterable object to visit all vertices of a matrix. * @return An iterable object to visit all vertices of a matrix. */ [[nodiscard]] iterable_adaptor vertices() const noexcept { return {0u, vert}; } /** * @brief Returns an iterable object to visit all edges of a matrix. * @return An iterable object to visit all edges of a matrix. */ [[nodiscard]] iterable_adaptor edges() const noexcept { const auto it = matrix.cbegin(); const auto sz = matrix.size(); return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; } /** * @brief Returns an iterable object to visit all out edges of a vertex. * @param vertex The vertex of which to return all out edges. * @return An iterable object to visit all out edges of a vertex. */ [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; const auto to = vertex * vert + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } /** * @brief Returns an iterable object to visit all in edges of a vertex. * @param vertex The vertex of which to return all in edges. * @return An iterable object to visit all in edges of a vertex. */ [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; const auto to = vert * (vert - 1u) + vertex; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } /** * @brief Resizes an adjacency matrix. * @param vertices The new number of vertices. */ void resize(const size_type vertices) { adjacency_matrix other{vertices, get_allocator()}; for(auto [lhs, rhs]: edges()) { other.insert(lhs, rhs); } other.swap(*this); } /** * @brief Inserts an edge into the adjacency matrix, if it does not exist. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 1u; } const auto inserted = !std::exchange(matrix[pos], 1u); return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; } /** * @brief Removes the edge associated with a pair of given vertices. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return Number of elements removed (either 0 or 1). */ size_type erase(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 0u; } return std::exchange(matrix[pos], 0u); } /** * @brief Checks if an adjacency matrix contains a given edge. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return True if there is such an edge, false otherwise. */ [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { const auto pos = lhs * vert + rhs; return pos < matrix.size() && matrix[pos]; } private: container_type matrix; size_type vert; }; } // namespace entt #endif // #include "../graph/flow.hpp" #ifndef ENTT_GRAPH_FLOW_HPP #define ENTT_GRAPH_FLOW_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../container/dense_set.hpp" #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" // #include "../core/memory.hpp" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class dense_set_iterator final { template friend class dense_set_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; constexpr dense_set_iterator() noexcept : it{} {} constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_set_local_iterator final { template friend class dense_set_local_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for unique objects of a given type. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on its hash. Elements with the same hash code * appear in the same bucket. * * @tparam Type Value type of the associative container. * @tparam Hash Type of function to use to hash the values. * @tparam KeyEqual Type of function to use to compare the values for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_set { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = std::pair; using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&value) { const auto index = value_to_bucket(value); if(auto it = constrained_find(value, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Type; /*! @brief Value type of the container. */ using value_type = Type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the elements. */ using hasher = Hash; /*! @brief Type of function to use to compare the elements for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Random access iterator type. */ using iterator = internal::dense_set_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::dense_set_iterator; /*! @brief Forward iterator type. */ using local_iterator = internal::dense_set_local_iterator; /*! @brief Constant forward iterator type. */ using const_local_iterator = internal::dense_set_local_iterator; /*! @brief Default constructor. */ dense_set() : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_set(const allocator_type &allocator) : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const allocator_type &allocator) : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_set(const dense_set &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_set(const dense_set &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_set(dense_set &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_set &operator=(const dense_set &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if it does not exist. * @param value An element to insert into the container. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value)); } /** * @brief Inserts elements into the container, if they do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Constructs an element in-place, if it does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace(Args &&...args) { if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); const auto index = value_to_bucket(node.second); if(auto it = constrained_find(node.second, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.first, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(*pos); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].second); } return (begin() + dist); } /** * @brief Removes the element associated with a given value. * @param value Value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const value_type &value) { for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { if(packed.second()(packed.first()[*curr].second, value)) { const auto index = *curr; *curr = packed.first()[*curr].first; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Returns the number of elements matching a value (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const value_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given value. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const value_type &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const value_type &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Finds an element that compares _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Returns a range containing all elements with a given value. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const value_type &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const value_type &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const value_type &value) const { return (find(value) != cend()); } /** * @brief Checks if the container contains an element that compares * _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &value) const { return (find(value) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given element. * @param value The value of the element to examine. * @return The bucket for the given element. */ [[nodiscard]] size_type bucket(const value_type &value) const { return value_to_bucket(value); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); packed.first()[pos].first = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the elements. * @return The function used to hash the elements. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare elements for equality. * @return The function used to compare elements for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "adjacency_matrix.hpp" #ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP #define ENTT_GRAPH_ADJACENCY_MATRIX_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class edge_iterator { using size_type = std::size_t; public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr edge_iterator() noexcept : it{}, vert{}, pos{}, last{}, offset{} {} constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept : it{std::move(base)}, vert{vertices}, pos{from}, last{to}, offset{step} { for(; pos != last && !it[pos]; pos += offset) {} } constexpr edge_iterator &operator++() noexcept { for(pos += offset; pos != last && !it[pos]; pos += offset) {} return *this; } constexpr edge_iterator operator++(int) noexcept { edge_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::make_pair(pos / vert, pos % vert); } template friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; private: It it; size_type vert; size_type pos; size_type last; size_type offset{}; }; template [[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return lhs.pos == rhs.pos; } template [[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic implementation of a directed adjacency matrix. * @tparam Category Either a directed or undirected category tag. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class adjacency_matrix { using alloc_traits = std::allocator_traits; static_assert(std::is_base_of_v, "Invalid graph category"); static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Vertex type. */ using vertex_type = size_type; /*! @brief Edge type. */ using edge_type = std::pair; /*! @brief Vertex iterator type. */ using vertex_iterator = iota_iterator; /*! @brief Edge iterator type. */ using edge_iterator = internal::edge_iterator; /*! @brief Out edge iterator type. */ using out_edge_iterator = edge_iterator; /*! @brief In edge iterator type. */ using in_edge_iterator = edge_iterator; /*! @brief Graph category tag. */ using graph_category = Category; /*! @brief Default constructor. */ adjacency_matrix() noexcept(noexcept(allocator_type{})) : adjacency_matrix{0u} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit adjacency_matrix(const allocator_type &allocator) noexcept : adjacency_matrix{0u, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied number of vertices. * @param vertices Number of vertices. * @param allocator The allocator to use. */ adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) : matrix{vertices * vertices, allocator}, vert{vertices} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ adjacency_matrix(const adjacency_matrix &other) : adjacency_matrix{other, other.get_allocator()} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) : matrix{other.matrix, allocator}, vert{other.vert} {} /** * @brief Move constructor. * @param other The instance to move from. */ adjacency_matrix(adjacency_matrix &&other) noexcept : adjacency_matrix{std::move(other), other.get_allocator()} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) : matrix{std::move(other.matrix), allocator}, vert{std::exchange(other.vert, 0u)} {} /** * @brief Default copy assignment operator. * @param other The instance to copy from. * @return This container. */ adjacency_matrix &operator=(const adjacency_matrix &other) { matrix = other.matrix; vert = other.vert; return *this; } /** * @brief Default move assignment operator. * @param other The instance to move from. * @return This container. */ adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { matrix = std::move(other.matrix); vert = std::exchange(other.vert, 0u); return *this; } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return matrix.get_allocator(); } /*! @brief Clears the adjacency matrix. */ void clear() noexcept { matrix.clear(); vert = {}; } /** * @brief Exchanges the contents with those of a given adjacency matrix. * @param other Adjacency matrix to exchange the content with. */ void swap(adjacency_matrix &other) { using std::swap; swap(matrix, other.matrix); swap(vert, other.vert); } /** * @brief Returns the number of vertices. * @return The number of vertices. */ [[nodiscard]] size_type size() const noexcept { return vert; } /** * @brief Returns an iterable object to visit all vertices of a matrix. * @return An iterable object to visit all vertices of a matrix. */ [[nodiscard]] iterable_adaptor vertices() const noexcept { return {0u, vert}; } /** * @brief Returns an iterable object to visit all edges of a matrix. * @return An iterable object to visit all edges of a matrix. */ [[nodiscard]] iterable_adaptor edges() const noexcept { const auto it = matrix.cbegin(); const auto sz = matrix.size(); return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; } /** * @brief Returns an iterable object to visit all out edges of a vertex. * @param vertex The vertex of which to return all out edges. * @return An iterable object to visit all out edges of a vertex. */ [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; const auto to = vertex * vert + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } /** * @brief Returns an iterable object to visit all in edges of a vertex. * @param vertex The vertex of which to return all in edges. * @return An iterable object to visit all in edges of a vertex. */ [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; const auto to = vert * (vert - 1u) + vertex; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } /** * @brief Resizes an adjacency matrix. * @param vertices The new number of vertices. */ void resize(const size_type vertices) { adjacency_matrix other{vertices, get_allocator()}; for(auto [lhs, rhs]: edges()) { other.insert(lhs, rhs); } other.swap(*this); } /** * @brief Inserts an edge into the adjacency matrix, if it does not exist. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 1u; } const auto inserted = !std::exchange(matrix[pos], 1u); return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; } /** * @brief Removes the edge associated with a pair of given vertices. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return Number of elements removed (either 0 or 1). */ size_type erase(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 0u; } return std::exchange(matrix[pos], 0u); } /** * @brief Checks if an adjacency matrix contains a given edge. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return True if there is such an edge, false otherwise. */ [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { const auto pos = lhs * vert + rhs; return pos < matrix.size() && matrix[pos]; } private: container_type matrix; size_type vert; }; } // namespace entt #endif // #include "fwd.hpp" namespace entt { /** * @brief Utility class for creating task graphs. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_flow { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; void emplace(const id_type res, const bool is_rw) { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); if(!deps.contains(res) && sync_on != vertices.size()) { deps[res].emplace_back(sync_on, true); } deps[res].emplace_back(index.first(), is_rw); } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Iterable task list. */ using iterable = iterable_adaptor; /*! @brief Default constructor. */ basic_flow() : basic_flow{allocator_type{}} {} /** * @brief Constructs a flow builder with a given allocator. * @param allocator The allocator to use. */ explicit basic_flow(const allocator_type &allocator) : index{0u, allocator}, vertices{}, deps{}, sync_on{} {} /*! @brief Default copy constructor. */ basic_flow(const basic_flow &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ basic_flow(const basic_flow &other, const allocator_type &allocator) : index{other.index.first(), allocator}, vertices{other.vertices, allocator}, deps{other.deps, allocator}, sync_on{other.sync_on} {} /*! @brief Default move constructor. */ basic_flow(basic_flow &&) noexcept = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_flow(basic_flow &&other, const allocator_type &allocator) : index{other.index.first(), allocator}, vertices{std::move(other.vertices), allocator}, deps{std::move(other.deps), allocator}, sync_on{other.sync_on} {} /** * @brief Default copy assignment operator. * @return This flow builder. */ basic_flow &operator=(const basic_flow &) = default; /** * @brief Default move assignment operator. * @return This flow builder. */ basic_flow &operator=(basic_flow &&) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{index.second()}; } /** * @brief Returns the identifier at specified location. * @param pos Position of the identifier to return. * @return The requested identifier. */ [[nodiscard]] id_type operator[](const size_type pos) const { return vertices.cbegin()[pos]; } /*! @brief Clears the flow builder. */ void clear() noexcept { index.first() = sync_on = {}; vertices.clear(); deps.clear(); } /** * @brief Exchanges the contents with those of a given flow builder. * @param other Flow builder to exchange the content with. */ void swap(basic_flow &other) { using std::swap; std::swap(index, other.index); std::swap(vertices, other.vertices); std::swap(deps, other.deps); std::swap(sync_on, other.sync_on); } /** * @brief Returns the number of tasks. * @return The number of tasks. */ [[nodiscard]] size_type size() const noexcept { return vertices.size(); } /** * @brief Binds a task to a flow builder. * @param value Task identifier. * @return This flow builder. */ basic_flow &bind(const id_type value) { sync_on += (sync_on == vertices.size()); const auto it = vertices.emplace(value).first; index.first() = size_type(it - vertices.begin()); return *this; } /** * @brief Turns the current task into a sync point. * @return This flow builder. */ basic_flow &sync() { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); sync_on = index.first(); for(const auto &elem: deps) { elem.second.emplace_back(sync_on, true); } return *this; } /** * @brief Assigns a resource to the current task with a given access mode. * @param res Resource identifier. * @param is_rw Access mode. * @return This flow builder. */ basic_flow &set(const id_type res, bool is_rw = false) { emplace(res, is_rw); return *this; } /** * @brief Assigns a read-only resource to the current task. * @param res Resource identifier. * @return This flow builder. */ basic_flow &ro(const id_type res) { emplace(res, false); return *this; } /** * @brief Assigns a range of read-only resources to the current task. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return This flow builder. */ template std::enable_if_t::value_type>, id_type>, basic_flow &> ro(It first, It last) { for(; first != last; ++first) { emplace(*first, false); } return *this; } /** * @brief Assigns a writable resource to the current task. * @param res Resource identifier. * @return This flow builder. */ basic_flow &rw(const id_type res) { emplace(res, true); return *this; } /** * @brief Assigns a range of writable resources to the current task. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return This flow builder. */ template std::enable_if_t::value_type>, id_type>, basic_flow &> rw(It first, It last) { for(; first != last; ++first) { emplace(*first, true); } return *this; } /** * @brief Generates a task graph for the current content. * @return The adjacency matrix of the task graph. */ [[nodiscard]] adjacency_matrix graph() const { const auto length = vertices.size(); adjacency_matrix matrix{length}; // creates the adjacency matrix for(const auto &elem: deps) { const auto last = elem.second.cend(); auto it = elem.second.cbegin(); while(it != last) { if(it->second) { // rw item if(auto curr = it++; it != last) { if(it->second) { matrix.insert(curr->first, it->first); } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { for(; it != next; ++it) { matrix.insert(curr->first, it->first); matrix.insert(it->first, next->first); } } else { for(; it != next; ++it) { matrix.insert(curr->first, it->first); } } } } else { // ro item (first iteration only) if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { for(; it != next; ++it) { matrix.insert(it->first, next->first); } } else { it = last; } } } } // computes the transitive closure for(std::size_t vk{}; vk < length; ++vk) { for(std::size_t vi{}; vi < length; ++vi) { for(std::size_t vj{}; vj < length; ++vj) { if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { matrix.insert(vi, vj); } } } } // applies the transitive reduction for(std::size_t vert{}; vert < length; ++vert) { matrix.erase(vert, vert); } for(std::size_t vj{}; vj < length; ++vj) { for(std::size_t vi{}; vi < length; ++vi) { if(matrix.contains(vi, vj)) { for(std::size_t vk{}; vk < length; ++vk) { if(matrix.contains(vj, vk)) { matrix.erase(vi, vk); } } } } } return matrix; } private: compressed_pair index; task_container_type vertices; deps_container_type deps; size_type sync_on; }; } // namespace entt #endif // #include "fwd.hpp" // #include "helper.hpp" #ifndef ENTT_ENTITY_HELPER_HPP #define ENTT_ENTITY_HELPER_HPP #include #include // #include "../core/fwd.hpp" // #include "../core/type_traits.hpp" // #include "../signal/delegate.hpp" // #include "component.hpp" // #include "fwd.hpp" // #include "group.hpp" // #include "view.hpp" namespace entt { /** * @brief Converts a registry to a view. * @tparam Registry Basic registry type. */ template class as_view { template auto dispatch(get_t, exclude_t) const { return reg.template view...>(exclude_t...>{}); } public: /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_view(registry_type &source) noexcept : reg{source} {} /** * @brief Conversion function from a registry to a view. * @tparam Get Type of storage used to construct the view. * @tparam Exclude Types of storage used to filter the view. * @return A newly created view. */ template operator basic_view() const { return dispatch(Get{}, Exclude{}); } private: registry_type ® }; /** * @brief Converts a registry to a group. * @tparam Registry Basic registry type. */ template class as_group { template auto dispatch(owned_t, get_t, exclude_t) const { if constexpr(std::is_const_v) { return reg.template group_if_exists(get_t{}, exclude_t{}); } else { return reg.template group...>(get_t...>{}, exclude_t...>{}); } } public: /*! @brief Type of registry to convert. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = std::remove_const_t; /** * @brief Constructs a converter for a given registry. * @param source A valid reference to a registry. */ as_group(registry_type &source) noexcept : reg{source} {} /** * @brief Conversion function from a registry to a group. * @tparam Owned Types of _owned_ by the group. * @tparam Get Types of storage _observed_ by the group. * @tparam Exclude Types of storage used to filter the group. * @return A newly created group. */ template operator basic_group() const { return dispatch(Owned{}, Get{}, Exclude{}); } private: registry_type ® }; /** * @brief Helper to create a listener that directly invokes a member function. * @tparam Member Member function to invoke on a component of the given type. * @tparam Registry Basic registry type. * @param reg A registry that contains the given entity and its components. * @param entt Entity from which to get the component. */ template>> void invoke(Registry ®, const typename Registry::entity_type entt) { static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); delegate func; func.template connect(reg.template get>(entt)); func(reg, entt); } /** * @brief Returns the entity associated with a given component. * * @warning * Currently, this function only works correctly with the default pool as it * makes assumptions about how the components are laid out. * * @tparam Registry Basic registry type. * @tparam Component Type of component. * @param reg A registry that contains the given entity and its components. * @param instance A valid component instance. * @return The entity associated with the given component. */ template typename Registry::entity_type to_entity(const Registry ®, const Component &instance) { const auto &storage = reg.template storage(); const typename Registry::base_type &base = storage; const auto *addr = std::addressof(instance); for(auto it = base.rbegin(), last = base.rend(); it < last; it += component_traits::page_size) { if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(component_traits::page_size)) { return *(it + dist); } } return null; } } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct is_view: std::false_type {}; template struct is_view>: std::true_type {}; template inline constexpr bool is_view_v = is_view::value; template struct unpack_type { using ro = std::conditional_t< type_list_contains_v || (std::is_const_v && !type_list_contains_v>), type_list>, type_list<>>; using rw = std::conditional_t< type_list_contains_v> || (!std::is_const_v && !type_list_contains_v), type_list, type_list<>>; }; template struct unpack_type, type_list> { using ro = type_list<>; using rw = type_list<>; }; template struct unpack_type, type_list> : unpack_type, type_list> {}; template struct unpack_type, exclude_t>, type_list> { using ro = type_list_cat_t, typename unpack_type, type_list>::ro...>; using rw = type_list_cat_t, type_list>::rw...>; }; template struct unpack_type, exclude_t>, type_list> : unpack_type, exclude_t>, type_list> {}; template struct resource_traits; template struct resource_traits, type_list> { using args = type_list...>; using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; }; template resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); template resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); template resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); template resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Utility class for creating a static task graph. * * This class offers minimal support (but sufficient in many cases) for creating * an execution graph from functions and their requirements on resources.
* Note that the resulting tasks aren't executed in any case. This isn't the * goal of the tool. Instead, they are returned to the user in the form of a * graph that allows for safe execution. * * @tparam Registry Basic registry type. */ template class basic_organizer final { using callback_type = void(const void *, Registry &); using prepare_type = void(Registry &); using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); struct vertex_data final { std::size_t ro_count{}; std::size_t rw_count{}; const char *name{}; const void *payload{}; callback_type *callback{}; dependency_type *dependency; prepare_type *prepare{}; const type_info *info{}; }; template [[nodiscard]] static decltype(auto) extract(Registry ®) { if constexpr(std::is_same_v) { return reg; } else if constexpr(internal::is_view_v) { return as_view{reg}; } else { return reg.ctx().template emplace>(); } } template [[nodiscard]] static auto to_args(Registry ®, type_list) { return std::tuple(reg))...>(extract(reg)...); } template static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { if constexpr(sizeof...(Type) == 0u) { return {}; } else { const type_info *info[sizeof...(Type)]{&type_id()...}; const auto length = count < sizeof...(Type) ? count : sizeof...(Type); for(std::size_t pos{}; pos < length; ++pos) { buffer[pos] = info[pos]; } return length; } } template void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { builder.bind(static_cast(index)); builder.set(type_hash::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); (builder.ro(type_hash::value()), ...); (builder.rw(type_hash::value()), ...); } public: /*! Basic registry type. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Raw task function type. */ using function_type = callback_type; /*! @brief Vertex type of a task graph defined as an adjacency list. */ struct vertex { /** * @brief Constructs a vertex of the task graph. * @param vtype True if the vertex is a top-level one, false otherwise. * @param data The data associated with the vertex. * @param edges The indices of the children in the adjacency list. */ vertex(const bool vtype, vertex_data data, std::vector edges) : is_top_level{vtype}, node{std::move(data)}, reachable{std::move(edges)} {} /** * @brief Fills a buffer with the type info objects for the writable * resources of a vertex. * @param buffer A buffer pre-allocated by the user. * @param length The length of the user-supplied buffer. * @return The number of type info objects written to the buffer. */ size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept { return node.dependency(false, buffer, length); } /** * @brief Fills a buffer with the type info objects for the read-only * resources of a vertex. * @param buffer A buffer pre-allocated by the user. * @param length The length of the user-supplied buffer. * @return The number of type info objects written to the buffer. */ size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept { return node.dependency(true, buffer, length); } /** * @brief Returns the number of read-only resources of a vertex. * @return The number of read-only resources of the vertex. */ size_type ro_count() const noexcept { return node.ro_count; } /** * @brief Returns the number of writable resources of a vertex. * @return The number of writable resources of the vertex. */ size_type rw_count() const noexcept { return node.rw_count; } /** * @brief Checks if a vertex is also a top-level one. * @return True if the vertex is a top-level one, false otherwise. */ bool top_level() const noexcept { return is_top_level; } /** * @brief Returns a type info object associated with a vertex. * @return A properly initialized type info object. */ const type_info &info() const noexcept { return *node.info; } /** * @brief Returns a user defined name associated with a vertex, if any. * @return The user defined name associated with the vertex, if any. */ const char *name() const noexcept { return node.name; } /** * @brief Returns the function associated with a vertex. * @return The function associated with the vertex. */ function_type *callback() const noexcept { return node.callback; } /** * @brief Returns the payload associated with a vertex, if any. * @return The payload associated with the vertex, if any. */ const void *data() const noexcept { return node.payload; } /** * @brief Returns the list of nodes reachable from a given vertex. * @return The list of nodes reachable from the vertex. */ const std::vector &children() const noexcept { return reachable; } /** * @brief Prepares a registry and assures that all required resources * are properly instantiated before using them. * @param reg A valid registry. */ void prepare(registry_type ®) const { node.prepare ? node.prepare(reg) : void(); } private: bool is_top_level; vertex_data node; std::vector reachable; }; /** * @brief Adds a free function to the task list. * @tparam Candidate Function to add to the task list. * @tparam Req Additional requirements and/or override resource access mode. * @param name Optional name to associate with the task. */ template void emplace(const char *name = nullptr) { using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); constexpr auto requires_registry = type_list_contains_v; callback_type *callback = +[](const void *, registry_type ®) { std::apply(Candidate, to_args(reg, typename resource_type::args{})); }; vertex_data vdata{ resource_type::ro::size, resource_type::rw::size, name, nullptr, callback, +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, &type_id>()}; track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); vertices.push_back(std::move(vdata)); } /** * @brief Adds a free function with payload or a member function with an * instance to the task list. * @tparam Candidate Function or member to add to the task list. * @tparam Req Additional requirements and/or override resource access mode. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @param name Optional name to associate with the task. */ template void emplace(Type &value_or_instance, const char *name = nullptr) { using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); constexpr auto requires_registry = type_list_contains_v; callback_type *callback = +[](const void *payload, registry_type ®) { Type *curr = static_cast(const_cast *>(payload)); std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); }; vertex_data vdata{ resource_type::ro::size, resource_type::rw::size, name, &value_or_instance, callback, +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, &type_id>()}; track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); vertices.push_back(std::move(vdata)); } /** * @brief Adds an user defined function with optional payload to the task * list. * @tparam Req Additional requirements and/or override resource access mode. * @param func Function to add to the task list. * @param payload User defined arbitrary data. * @param name Optional name to associate with the task. */ template void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { using resource_type = internal::resource_traits, type_list>; track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); vertex_data vdata{ resource_type::ro::size, resource_type::rw::size, name, payload, func, +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, nullptr, &type_id()}; vertices.push_back(std::move(vdata)); } /** * @brief Generates a task graph for the current content. * @return The adjacency list of the task graph. */ std::vector graph() { std::vector adjacency_list{}; adjacency_list.reserve(vertices.size()); auto adjacency_matrix = builder.graph(); for(auto curr: adjacency_matrix.vertices()) { const auto iterable = adjacency_matrix.in_edges(curr); std::vector reachable{}; for(auto &&edge: adjacency_matrix.out_edges(curr)) { reachable.push_back(edge.second); } adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable)); } return adjacency_list; } /*! @brief Erases all elements from a container. */ void clear() { builder.clear(); vertices.clear(); } private: std::vector vertices; flow builder; }; } // namespace entt #endif // #include "entity/registry.hpp" #ifndef ENTT_ENTITY_REGISTRY_HPP #define ENTT_ENTITY_REGISTRY_HPP #include #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../core/algorithm.hpp" // #include "../core/any.hpp" // #include "../core/compressed_pair.hpp" // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "../core/memory.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "group.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" // #include "view.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class registry_storage_iterator final { template friend class registry_storage_iterator; using mapped_type = std::remove_reference_t()->second)>; public: using value_type = std::pair &>; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr registry_storage_iterator() noexcept : it{} {} constexpr registry_storage_iterator(It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr registry_storage_iterator(const registry_storage_iterator &other) noexcept : registry_storage_iterator{other.it} {} constexpr registry_storage_iterator &operator++() noexcept { return ++it, *this; } constexpr registry_storage_iterator operator++(int) noexcept { registry_storage_iterator orig = *this; return ++(*this), orig; } constexpr registry_storage_iterator &operator--() noexcept { return --it, *this; } constexpr registry_storage_iterator operator--(int) noexcept { registry_storage_iterator orig = *this; return operator--(), orig; } constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { registry_storage_iterator copy = *this; return (copy += value); } constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].first, *it[value].second}; } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->first, *it->second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } template friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; template friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; template friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { return !(lhs < rhs); } class registry_context { using key_type = id_type; using mapped_type = basic_any<0u>; using container_type = dense_map; public: template [[deprecated("Use ::emplace_as instead")]] Type &emplace_hint(const id_type id, Args &&...args) { return emplace_as(id, std::forward(args)...); } template Type &emplace_as(const id_type id, Args &&...args) { return any_cast(ctx.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); } template Type &emplace(Args &&...args) { return emplace_as(type_id().hash(), std::forward(args)...); } template Type &insert_or_assign(const id_type id, Type &&value) { return any_cast> &>(ctx.insert_or_assign(id, std::forward(value)).first->second); } template Type &insert_or_assign(Type &&value) { return insert_or_assign(type_id().hash(), std::forward(value)); } template bool erase(const id_type id = type_id().hash()) { const auto it = ctx.find(id); return it != ctx.end() && it->second.type() == type_id() ? (ctx.erase(it), true) : false; } template [[deprecated("Use ::get instead")]] [[nodiscard]] const Type &at(const id_type id = type_id().hash()) const { return get(id); } template [[deprecated("Use ::get instead")]] [[nodiscard]] Type &at(const id_type id = type_id().hash()) { return get(id); } template [[nodiscard]] const Type &get(const id_type id = type_id().hash()) const { return any_cast(ctx.at(id)); } template [[nodiscard]] Type &get(const id_type id = type_id().hash()) { return any_cast(ctx.at(id)); } template [[nodiscard]] const Type *find(const id_type id = type_id().hash()) const { const auto it = ctx.find(id); return it != ctx.cend() ? any_cast(&it->second) : nullptr; } template [[nodiscard]] Type *find(const id_type id = type_id().hash()) { const auto it = ctx.find(id); return it != ctx.end() ? any_cast(&it->second) : nullptr; } template [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { const auto it = ctx.find(id); return it != ctx.cend() && it->second.type() == type_id(); } private: container_type ctx; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Fast and reliable entity-component system. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_registry { using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using basic_common_type = basic_sparse_set; using entity_traits = entt_traits; template using storage_for_type = typename storage_for>>::type; template struct group_handler; template struct group_handler, get_t, Owned...> { // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here static_assert(!std::disjunction_v::in_place_delete>...>, "Groups do not support in-place delete"); using value_type = std::conditional_t; value_type current{}; template group_handler(Args &&...args) : current{std::forward(args)...} {} template void maybe_valid_if(basic_registry &owner, const Entity entt) { [[maybe_unused]] const auto cpools = std::forward_as_tuple(owner.assure()...); const auto is_valid = ((std::is_same_v || std::get &>(cpools).contains(entt)) && ...) && ((std::is_same_v || owner.assure().contains(entt)) && ...) && ((std::is_same_v || !owner.assure().contains(entt)) && ...); if constexpr(sizeof...(Owned) == 0) { if(is_valid && !current.contains(entt)) { current.emplace(entt); } } else { if(is_valid && !(std::get<0>(cpools).index(entt) < current)) { const auto pos = current++; (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } void discard_if([[maybe_unused]] basic_registry &owner, const Entity entt) { if constexpr(sizeof...(Owned) == 0) { current.remove(entt); } else { if(const auto cpools = std::forward_as_tuple(owner.assure()...); std::get<0>(cpools).contains(entt) && (std::get<0>(cpools).index(entt) < current)) { const auto pos = --current; (std::get &>(cpools).swap_elements(std::get &>(cpools).data()[pos], entt), ...); } } } }; struct group_data { std::size_t size; std::shared_ptr group; bool (*owned)(const id_type) noexcept; bool (*get)(const id_type) noexcept; bool (*exclude)(const id_type) noexcept; }; template [[nodiscard]] auto &assure(const id_type id = type_hash::value()) { static_assert(std::is_same_v>, "Non-decayed types not allowed"); auto &cpool = pools[id]; if(!cpool) { cpool = std::allocate_shared>>(get_allocator(), get_allocator()); cpool->bind(forward_as_any(*this)); } ENTT_ASSERT(cpool->type() == type_id(), "Unexpected type"); return static_cast &>(*cpool); } template [[nodiscard]] const auto &assure(const id_type id = type_hash::value()) const { static_assert(std::is_same_v>, "Non-decayed types not allowed"); if(const auto it = pools.find(id); it != pools.cend()) { ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); return static_cast &>(*it->second); } static storage_for_type placeholder{}; return placeholder; } auto generate_identifier(const std::size_t pos) noexcept { ENTT_ASSERT(pos < entity_traits::to_entity(null), "No entities available"); return entity_traits::combine(static_cast(pos), {}); } auto recycle_identifier() noexcept { ENTT_ASSERT(free_list != null, "No entities available"); const auto curr = entity_traits::to_entity(free_list); free_list = entity_traits::combine(entity_traits::to_integral(epool[curr]), tombstone); return (epool[curr] = entity_traits::combine(curr, entity_traits::to_integral(epool[curr]))); } auto release_entity(const Entity entt, const typename entity_traits::version_type version) { const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); epool[entity_traits::to_entity(entt)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); free_list = entity_traits::combine(entity_traits::to_integral(entt), tombstone); return vers; } void rebind() { for(auto &&curr: pools) { curr.second->bind(forward_as_any(*this)); } } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Underlying version type. */ using version_type = typename entity_traits::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Context type. */ using context = internal::registry_context; /*! @brief Default constructor. */ basic_registry() : basic_registry{allocator_type{}} {} /** * @brief Constructs an empty registry with a given allocator. * @param allocator The allocator to use. */ explicit basic_registry(const allocator_type &allocator) : basic_registry{0u, allocator} {} /** * @brief Allocates enough memory upon construction to store `count` pools. * @param count The number of pools to allocate memory for. * @param allocator The allocator to use. */ basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) : vars{}, free_list{tombstone}, epool{allocator}, pools{allocator}, groups{allocator} { pools.reserve(count); } /** * @brief Move constructor. * @param other The instance to move from. */ basic_registry(basic_registry &&other) noexcept : vars{std::move(other.vars)}, free_list{std::move(other.free_list)}, epool{std::move(other.epool)}, pools{std::move(other.pools)}, groups{std::move(other.groups)} { rebind(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This registry. */ basic_registry &operator=(basic_registry &&other) noexcept { vars = std::move(other.vars); free_list = std::move(other.free_list); epool = std::move(other.epool); pools = std::move(other.pools); groups = std::move(other.groups); rebind(); return *this; } /** * @brief Exchanges the contents with those of a given registry. * @param other Registry to exchange the content with. */ void swap(basic_registry &other) { using std::swap; swap(vars, other.vars); swap(free_list, other.free_list); swap(epool, other.epool); swap(pools, other.pools); swap(groups, other.groups); rebind(); other.rebind(); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return epool.get_allocator(); } /** * @brief Returns an iterable object to use to _visit_ a registry. * * The iterable object returns a pair that contains the name and a reference * to the current storage. * * @return An iterable object to use to _visit_ the registry. */ [[nodiscard]] auto storage() noexcept { return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}}; } /*! @copydoc storage */ [[nodiscard]] auto storage() const noexcept { return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}}; } /** * @brief Finds the storage associated with a given name, if any. * @param id Name used to map the storage within the registry. * @return A pointer to the storage if it exists, a null pointer otherwise. */ [[nodiscard]] base_type *storage(const id_type id) { return const_cast(std::as_const(*this).storage(id)); } /** * @brief Finds the storage associated with a given name, if any. * @param id Name used to map the storage within the registry. * @return A pointer to the storage if it exists, a null pointer otherwise. */ [[nodiscard]] const base_type *storage(const id_type id) const { const auto it = pools.find(id); return it == pools.cend() ? nullptr : it->second.get(); } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @param id Optional name used to map the storage within the registry. * @return The storage for the given component type. */ template decltype(auto) storage(const id_type id = type_hash::value()) { return assure(id); } /** * @brief Returns the storage for a given component type. * * @warning * If a storage for the given component doesn't exist yet, a temporary * placeholder is returned instead. * * @tparam Type Type of component of which to return the storage. * @param id Optional name used to map the storage within the registry. * @return The storage for the given component type. */ template decltype(auto) storage(const id_type id = type_hash::value()) const { return assure(id); } /** * @brief Returns the number of entities created so far. * @return Number of entities created so far. */ [[nodiscard]] size_type size() const noexcept { return epool.size(); } /** * @brief Returns the number of entities still in use. * @return Number of entities still in use. */ [[nodiscard]] size_type alive() const { auto sz = epool.size(); for(auto curr = free_list; curr != null; --sz) { curr = epool[entity_traits::to_entity(curr)]; } return sz; } /** * @brief Increases the capacity (number of entities) of the registry. * @param cap Desired capacity. */ void reserve(const size_type cap) { epool.reserve(cap); } /** * @brief Returns the number of entities that a registry has currently * allocated space for. * @return Capacity of the registry. */ [[nodiscard]] size_type capacity() const noexcept { return epool.capacity(); } /** * @brief Checks whether the registry is empty (no entities still in use). * @return True if the registry is empty, false otherwise. */ [[nodiscard]] bool empty() const { return !alive(); } /** * @brief Direct access to the list of entities of a registry. * * The returned pointer is such that range `[data(), data() + size())` is * always a valid range, even if the registry is empty. * * @warning * This list contains both valid and destroyed entities and isn't suitable * for direct use. * * @return A pointer to the array of entities. */ [[nodiscard]] const entity_type *data() const noexcept { return epool.data(); } /** * @brief Returns the head of the list of released entities. * * This function is intended for use in conjunction with `assign`.
* The returned entity has an invalid identifier in all cases. * * @return The head of the list of released entities. */ [[nodiscard]] entity_type released() const noexcept { return free_list; } /** * @brief Checks if an identifier refers to a valid entity. * @param entt An identifier, either valid or not. * @return True if the identifier is valid, false otherwise. */ [[nodiscard]] bool valid(const entity_type entt) const { const auto pos = size_type(entity_traits::to_entity(entt)); return (pos < epool.size() && epool[pos] == entt); } /** * @brief Returns the actual version for an identifier. * @param entt A valid identifier. * @return The version for the given identifier if valid, the tombstone * version otherwise. */ [[nodiscard]] version_type current(const entity_type entt) const { const auto pos = size_type(entity_traits::to_entity(entt)); return entity_traits::to_version(pos < epool.size() ? epool[pos] : tombstone); } /** * @brief Creates a new entity or recycles a destroyed one. * @return A valid identifier. */ [[nodiscard]] entity_type create() { return (free_list == null) ? epool.emplace_back(generate_identifier(epool.size())) : recycle_identifier(); } /** * @copybrief create * * If the requested entity isn't in use, the suggested identifier is used. * Otherwise, a new identifier is generated. * * @param hint Required identifier. * @return A valid identifier. */ [[nodiscard]] entity_type create(const entity_type hint) { const auto length = epool.size(); if(hint == null || hint == tombstone) { return create(); } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { epool.resize(size_type(req) + 1u, null); for(auto pos = length; pos < req; ++pos) { release_entity(generate_identifier(pos), {}); } return (epool[req] = hint); } else if(const auto curr = entity_traits::to_entity(epool[req]); req == curr) { return create(); } else { auto *it = &free_list; for(; entity_traits::to_entity(*it) != req; it = &epool[entity_traits::to_entity(*it)]) {} *it = entity_traits::combine(curr, entity_traits::to_integral(*it)); return (epool[req] = hint); } } /** * @brief Assigns each element in a range an identifier. * * @sa create * * @tparam It Type of forward iterator. * @param first An iterator to the first element of the range to generate. * @param last An iterator past the last element of the range to generate. */ template void create(It first, It last) { for(; free_list != null && first != last; ++first) { *first = recycle_identifier(); } const auto length = epool.size(); epool.resize(length + std::distance(first, last), null); for(auto pos = length; first != last; ++first, ++pos) { *first = epool[pos] = generate_identifier(pos); } } /** * @brief Assigns identifiers to an empty registry. * * This function is intended for use in conjunction with `data`, `size` and * `released`.
* Don't try to inject ranges of randomly generated entities nor the _wrong_ * head for the list of destroyed entities. There is no guarantee that a * registry will continue to work properly in this case. * * @warning * There must be no entities still alive for this to work properly. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param destroyed The head of the list of destroyed entities. */ template void assign(It first, It last, const entity_type destroyed) { ENTT_ASSERT(!alive(), "Entities still alive"); epool.assign(first, last); free_list = destroyed; } /** * @brief Releases an identifier. * * The version is updated and the identifier can be recycled at any time. * * @warning * Attempting to use an invalid entity results in undefined behavior. * * @param entt A valid identifier. * @return The version of the recycled entity. */ version_type release(const entity_type entt) { return release(entt, static_cast(entity_traits::to_version(entt) + 1u)); } /** * @brief Releases an identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa release * * @param entt A valid identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type release(const entity_type entt, const version_type version) { ENTT_ASSERT(valid(entt), "Invalid identifier"); ENTT_ASSERT(std::all_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return (curr.second->current(entt) == entity_traits::to_version(tombstone)); }), "Non-orphan entity"); return release_entity(entt, version); } /** * @brief Releases all identifiers in a range. * * @sa release * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void release(It first, It last) { for(; first != last; ++first) { release(*first); } } /** * @brief Destroys an entity and releases its identifier. * * @sa release * * @warning * Adding or removing components to an entity that is being destroyed can * result in undefined behavior. Attempting to use an invalid entity results * in undefined behavior. * * @param entt A valid identifier. * @return The version of the recycled entity. */ version_type destroy(const entity_type entt) { return destroy(entt, static_cast(entity_traits::to_version(entt) + 1u)); } /** * @brief Destroys an entity and releases its identifier. * * The suggested version or the valid version closest to the suggested one * is used instead of the implicitly generated version. * * @sa destroy * * @param entt A valid identifier. * @param version A desired version upon destruction. * @return The version actually assigned to the entity. */ version_type destroy(const entity_type entt, const version_type version) { for(size_type pos = pools.size(); pos; --pos) { pools.begin()[pos - 1u].second->remove(entt); } return release(entt, version); } /** * @brief Destroys all entities in a range and releases their identifiers. * * @sa destroy * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void destroy(It first, It last) { for(; first != last; ++first) { destroy(*first); } } /** * @brief Assigns the given component to an entity. * * The component must have a proper constructor or be of aggregate type. * * @warning * Attempting to assign a component to an entity that already owns it * results in undefined behavior. * * @tparam Type Type of component to create. * @tparam Args Types of arguments to use to construct the component. * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace(const entity_type entt, Args &&...args) { return assure().emplace(entt, std::forward(args)...); } /** * @brief Assigns each entity in a range the given component. * * @sa emplace * * @tparam Type Type of component to create. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the component to assign. */ template void insert(It first, It last, const Type &value = {}) { assure().insert(first, last, value); } /** * @brief Assigns each entity in a range the given components. * * @sa emplace * * @tparam Type Type of component to create. * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of components. */ template::value_type, Type>>> void insert(EIt first, EIt last, CIt from) { assure().insert(first, last, from); } /** * @brief Assigns or replaces the given component for an entity. * * @sa emplace * @sa replace * * @tparam Type Type of component to assign or replace. * @tparam Args Types of arguments to use to construct the component. * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the newly created component. */ template decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) { if(auto &cpool = assure(); cpool.contains(entt)) { return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); } else { return cpool.emplace(entt, std::forward(args)...); } } /** * @brief Patches the given component for an entity. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(Type &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned. However, this function can be used to trigger an update signal * for them. * * @warning * Attempting to to patch a component of an entity that doesn't own it * results in undefined behavior. * * @tparam Type Type of component to patch. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the patched component. */ template decltype(auto) patch(const entity_type entt, Func &&...func) { return assure().patch(entt, std::forward(func)...); } /** * @brief Replaces the given component for an entity. * * The component must have a proper constructor or be of aggregate type. * * @warning * Attempting to replace a component of an entity that doesn't own it * results in undefined behavior. * * @tparam Type Type of component to replace. * @tparam Args Types of arguments to use to construct the component. * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return A reference to the component being replaced. */ template decltype(auto) replace(const entity_type entt, Args &&...args) { return patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); } /** * @brief Removes the given components from an entity. * * @tparam Type Type of component to remove. * @tparam Other Other types of components to remove. * @param entt A valid identifier. * @return The number of components actually removed. */ template size_type remove(const entity_type entt) { return (assure().remove(entt) + ... + assure().remove(entt)); } /** * @brief Removes the given components from all the entities in a range. * * @sa remove * * @tparam Type Type of component to remove. * @tparam Other Other types of components to remove. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return The number of components actually removed. */ template size_type remove(It first, It last) { if constexpr(sizeof...(Other) == 0u) { return assure().remove(std::move(first), std::move(last)); } else { size_type count{}; for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); } return count; } } /** * @brief Erases the given components from an entity. * * @warning * Attempting to erase a component from an entity that doesn't own it * results in undefined behavior. * * @tparam Type Types of components to erase. * @tparam Other Other types of components to erase. * @param entt A valid identifier. */ template void erase(const entity_type entt) { (assure().erase(entt), (assure().erase(entt), ...)); } /** * @brief Erases the given components from all the entities in a range. * * @sa erase * * @tparam Type Types of components to erase. * @tparam Other Other types of components to erase. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void erase(It first, It last) { if constexpr(sizeof...(Other) == 0u) { assure().erase(std::move(first), std::move(last)); } else { for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); } } } /** * @brief Removes all tombstones from a registry or only the pools for the * given components. * @tparam Type Types of components for which to clear all tombstones. */ template void compact() { if constexpr(sizeof...(Type) == 0) { for(auto &&curr: pools) { curr.second->compact(); } } else { (assure().compact(), ...); } } /** * @brief Check if an entity is part of all the given storage. * @tparam Type Type of storage to check for. * @param entt A valid identifier. * @return True if the entity is part of all the storage, false otherwise. */ template [[nodiscard]] bool all_of(const entity_type entt) const { return (assure>().contains(entt) && ...); } /** * @brief Check if an entity is part of at least one given storage. * @tparam Type Type of storage to check for. * @param entt A valid identifier. * @return True if the entity is part of at least one storage, false * otherwise. */ template [[nodiscard]] bool any_of(const entity_type entt) const { return (assure>().contains(entt) || ...); } /** * @brief Returns references to the given components for an entity. * * @warning * Attempting to get a component from an entity that doesn't own it results * in undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return References to the components owned by the entity. */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { if constexpr(sizeof...(Type) == 1u) { return (assure>().get(entt), ...); } else { return std::forward_as_tuple(get(entt)...); } } /*! @copydoc get */ template [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { if constexpr(sizeof...(Type) == 1u) { return (const_cast(std::as_const(*this).template get(entt)), ...); } else { return std::forward_as_tuple(get(entt)...); } } /** * @brief Returns a reference to the given component for an entity. * * In case the entity doesn't own the component, the parameters provided are * used to construct it. * * @sa get * @sa emplace * * @tparam Type Type of component to get. * @tparam Args Types of arguments to use to construct the component. * @param entt A valid identifier. * @param args Parameters to use to initialize the component. * @return Reference to the component owned by the entity. */ template [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { if(auto &cpool = assure(); cpool.contains(entt)) { return cpool.get(entt); } else { return cpool.emplace(entt, std::forward(args)...); } } /** * @brief Returns pointers to the given components for an entity. * * @note * The registry retains ownership of the pointed-to components. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return Pointers to the components owned by the entity. */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { if constexpr(sizeof...(Type) == 1) { const auto &cpool = assure...>(); return cpool.contains(entt) ? std::addressof(cpool.get(entt)) : nullptr; } else { return std::make_tuple(try_get(entt)...); } } /*! @copydoc try_get */ template [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { if constexpr(sizeof...(Type) == 1) { return (const_cast(std::as_const(*this).template try_get(entt)), ...); } else { return std::make_tuple(try_get(entt)...); } } /** * @brief Clears a whole registry or the pools for the given components. * @tparam Type Types of components to remove from their entities. */ template void clear() { if constexpr(sizeof...(Type) == 0) { for(auto &&curr: pools) { curr.second->clear(); } each([this](const auto entity) { this->release(entity); }); } else { (assure().clear(), ...); } } /** * @brief Iterates all the entities that are still in use. * * The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const Entity); * @endcode * * It's not defined whether entities created during iteration are returned. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if(free_list == null) { for(auto pos = epool.size(); pos; --pos) { func(epool[pos - 1]); } } else { for(auto pos = epool.size(); pos; --pos) { if(const auto entity = epool[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { func(entity); } } } } /** * @brief Checks if an entity has components assigned. * @param entt A valid identifier. * @return True if the entity has no components assigned, false otherwise. */ [[nodiscard]] bool orphan(const entity_type entt) const { return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); } /** * @brief Returns a sink object for the given component. * * Use this function to receive notifications whenever a new instance of the * given component is created and assigned to an entity.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** assigning the component to the entity. * * @sa sink * * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_construct() { return assure().on_construct(); } /** * @brief Returns a sink object for the given component. * * Use this function to receive notifications whenever an instance of the * given component is explicitly updated.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **after** updating the component. * * @sa sink * * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_update() { return assure().on_update(); } /** * @brief Returns a sink object for the given component. * * Use this function to receive notifications whenever an instance of the * given component is removed from an entity and thus destroyed.
* The function type for a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, Entity); * @endcode * * Listeners are invoked **before** removing the component from the entity. * * @sa sink * * @tparam Type Type of component of which to get the sink. * @return A temporary sink object. */ template [[nodiscard]] auto on_destroy() { return assure().on_destroy(); } /** * @brief Returns a view for the given components. * * Views are created on the fly and share with the registry its internal * data structures. Feel free to discard them after the use.
* Creating and destroying a view is an incredibly cheap operation. As a * rule of thumb, storing a view should never be an option. * * @tparam Type Type of component used to construct the view. * @tparam Other Other types of components used to construct the view. * @tparam Exclude Types of components used to filter the view. * @return A newly created view. */ template [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> view(exclude_t = {}) const { return {assure>(), assure>()..., assure>()...}; } /*! @copydoc view */ template [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> view(exclude_t = {}) { return {assure>(), assure>()..., assure>()...}; } /** * @brief Returns a group for the given components. * * Groups are created on the fly and share with the registry its internal * data structures. Feel free to discard them after the use.
* Creating and destroying a group is an incredibly cheap operation. As a * rule of thumb, storing a group should never be an option. * * Groups support exclusion lists and can own types of components. The more * types are owned by a group, the faster it is to iterate entities and * components.
* However, groups also affect some features of the registry such as the * creation and destruction of components. * * @note * Pools of components that are owned by a group cannot be sorted anymore. * The group takes the ownership of the pools and arrange components so as * to iterate them as fast as possible. * * @tparam Owned Type of storage _owned_ by the group. * @tparam Get Type of storage _observed_ by the group. * @tparam Exclude Type of storage used to filter the group. * @return A newly created group. */ template [[nodiscard]] basic_group...>, get_t...>, exclude_t...>> group(get_t = {}, exclude_t = {}) { static_assert(sizeof...(Owned) + sizeof...(Get) > 0, "Exclusion-only groups are not supported"); static_assert(sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude) > 1, "Single component groups are not allowed"); using handler_type = group_handler...>, get_t...>, std::remove_const_t...>; const auto cpools = std::forward_as_tuple(assure>()..., assure>()...); constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); handler_type *handler = nullptr; auto it = std::find_if(groups.cbegin(), groups.cend(), [size](const auto &gdata) { return gdata.size == size && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash>::value()) && ...); }); if(it != groups.cend()) { handler = static_cast(it->group.get()); } else { group_data candidate = { size, std::apply([this](auto &&...args) { return std::allocate_shared(get_allocator(), std::forward(args)...); }, entt::uses_allocator_construction_args(get_allocator())), []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, []([[maybe_unused]] const id_type ctype) noexcept { return ((ctype == type_hash>::value()) || ...); }, }; handler = static_cast(candidate.group.get()); const void *maybe_valid_if = nullptr; const void *discard_if = nullptr; if constexpr(sizeof...(Owned) == 0) { groups.push_back(std::move(candidate)); } else { [[maybe_unused]] auto has_conflict = [size](const auto &gdata) { const auto overlapping = (0u + ... + gdata.owned(type_hash>::value())); const auto sz = overlapping + (0u + ... + gdata.get(type_hash>::value())) + (0u + ... + gdata.exclude(type_hash>::value())); return !overlapping || ((sz == size) || (sz == gdata.size)); }; ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), std::move(has_conflict)), "Conflicting groups"); const auto next = std::find_if_not(groups.cbegin(), groups.cend(), [size](const auto &gdata) { return !(0u + ... + gdata.owned(type_hash>::value())) || (size > gdata.size); }); const auto prev = std::find_if(std::make_reverse_iterator(next), groups.crend(), [](const auto &gdata) { return (0u + ... + gdata.owned(type_hash>::value())); }); maybe_valid_if = (next == groups.cend() ? maybe_valid_if : next->group.get()); discard_if = (prev == groups.crend() ? discard_if : prev->group.get()); groups.insert(next, std::move(candidate)); } (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_construct>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_destroy>().before(maybe_valid_if).template connect<&handler_type::template maybe_valid_if>>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_destroy>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); (on_construct>().before(discard_if).template connect<&handler_type::discard_if>(*handler), ...); if constexpr(sizeof...(Owned) == 0) { for(const auto entity: view(exclude)) { handler->current.emplace(entity); } } else { // we cannot iterate backwards because we want to leave behind valid entities in case of owned types for(auto *first = std::get<0>(cpools).data(), *last = first + std::get<0>(cpools).size(); first != last; ++first) { handler->template maybe_valid_if...>>>(*this, *first); } } } return {handler->current, std::get> &>(cpools)..., std::get> &>(cpools)...}; } /*! @copydoc group */ template [[nodiscard]] basic_group...>, get_t...>, exclude_t...>> group_if_exists(get_t = {}, exclude_t = {}) const { auto it = std::find_if(groups.cbegin(), groups.cend(), [](const auto &gdata) { return gdata.size == (sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude)) && (gdata.owned(type_hash>::value()) && ...) && (gdata.get(type_hash>::value()) && ...) && (gdata.exclude(type_hash>::value()) && ...); }); if(it == groups.cend()) { return {}; } else { using handler_type = group_handler...>, get_t...>, std::remove_const_t...>; return {static_cast(it->group.get())->current, assure>()..., assure>()...}; } } /** * @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 _free_, false * otherwise. */ template [[nodiscard]] bool owned() const { return std::any_of(groups.cbegin(), groups.cend(), [](auto &&gdata) { return (gdata.owned(type_hash>::value()) || ...); }); } /** * @brief Checks whether a group can be sorted. * @tparam Owned Type of storage _owned_ by the group. * @tparam Get Type of storage _observed_ by the group. * @tparam Exclude Type of storage used to filter the group. * @return True if the group can be sorted, false otherwise. */ template [[nodiscard]] bool sortable(const basic_group, get_t, exclude_t> &) noexcept { constexpr auto size = sizeof...(Owned) + sizeof...(Get) + sizeof...(Exclude); auto pred = [size](const auto &gdata) { return (0u + ... + gdata.owned(type_hash::value())) && (size < gdata.size); }; return std::find_if(groups.cbegin(), groups.cend(), std::move(pred)) == groups.cend(); } /** * @brief Sorts the elements of a given component. * * The order remains valid until a component of the given type is assigned * to or removed from an entity.
* The comparison function object returns `true` if the first element is * _less_ than the second one, `false` otherwise. Its signature is also * equivalent to one of the following: * * @code{.cpp} * bool(const Entity, const Entity); * bool(const Type &, const Type &); * @endcode * * Moreover, it shall induce a _strict weak ordering_ on the values.
* The sort function object offers an `operator()` that accepts: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function object to use to compare the elements. * * The comparison function object hasn't necessarily the type of the one * passed along with the other parameters to this member function. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam Type Type of components to sort. * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { ENTT_ASSERT(!owned(), "Cannot sort owned storage"); auto &cpool = assure(); if constexpr(std::is_invocable_v) { auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); } else { cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); } } /** * @brief Sorts two pools of components in the same way. * * Being `To` and `From` the two sets, after invoking this function an * iterator for `To` returns elements according to the following rules: * * * All entities in `To` that are also in `From` are returned first * according to the order they have in `From`. * * All entities in `To` that are not in `From` are returned in no * particular order after all the other entities. * * Any subsequent change to `From` won't affect the order in `To`. * * @warning * Pools of components owned by a group cannot be sorted. * * @tparam To Type of components to sort. * @tparam From Type of components to use to sort. */ template void sort() { ENTT_ASSERT(!owned(), "Cannot sort owned storage"); assure().respect(assure()); } /** * @brief Returns the context object, that is, a general purpose container. * @return The context object, that is, a general purpose container. */ context &ctx() noexcept { return vars; } /*! @copydoc ctx */ const context &ctx() const noexcept { return vars; } private: context vars; entity_type free_list; std::vector epool; // std::shared_ptr because of its type erased allocator which is useful here dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>> pools; std::vector> groups; }; } // namespace entt #endif // #include "entity/runtime_view.hpp" #ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP #define ENTT_ENTITY_RUNTIME_VIEW_HPP #include #include #include #include #include // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class runtime_view_iterator final { using iterator_type = typename Set::iterator; [[nodiscard]] bool valid() const { return (!tombstone_check || *it != tombstone) && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); } public: using difference_type = typename iterator_type::difference_type; using value_type = typename iterator_type::value_type; using pointer = typename iterator_type::pointer; using reference = typename iterator_type::reference; using iterator_category = std::bidirectional_iterator_tag; constexpr runtime_view_iterator() noexcept : pools{}, filter{}, it{}, tombstone_check{} {} runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) noexcept : pools{&cpools}, filter{&ignore}, it{curr}, tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { if(it != (*pools)[0]->end() && !valid()) { ++(*this); } } runtime_view_iterator &operator++() { while(++it != (*pools)[0]->end() && !valid()) {} return *this; } runtime_view_iterator operator++(int) { runtime_view_iterator orig = *this; return ++(*this), orig; } runtime_view_iterator &operator--() { while(--it != (*pools)[0]->begin() && !valid()) {} return *this; } runtime_view_iterator operator--(int) { runtime_view_iterator orig = *this; return operator--(), orig; } [[nodiscard]] pointer operator->() const noexcept { return it.operator->(); } [[nodiscard]] reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { return it == other.it; } [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { return !(*this == other); } private: const std::vector *pools; const std::vector *filter; iterator_type it; bool tombstone_check; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Generic runtime view. * * Runtime views iterate over those entities that have at least all the given * components in their bags. During initialization, a runtime view looks at the * number of entities available for each component and picks up a reference to * the smallest set of candidate entities in order to get a performance boost * when iterate.
* Order of elements during iterations are highly dependent on the order of the * underlying data structures. See sparse_set and its specializations for more * details. * * @b Important * * Iterators aren't invalidated if: * * * New instances of the given components are created and assigned to entities. * * The entity currently pointed is modified (as an example, if one of the * given components is removed from the entity to which the iterator points). * * The entity currently pointed is destroyed. * * In all the other cases, modifying the pools of the given components in any * way invalidates all the iterators and using them results in undefined * behavior. * * @note * Views share references to the underlying data structures of the registry that * generated them. Therefore any change to the entities and to the components * made by means of the registry are immediately reflected by the views, unless * a pool was missing when the view was built (in this case, the view won't * have a valid reference and won't be updated accordingly). * * @warning * Lifetime of a view must not overcome that of the registry that generated it. * In any other case, attempting to use a view results in undefined behavior. * * @tparam Type Common base type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_runtime_view { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = Type; /*! @brief Bidirectional iterator type. */ using iterator = internal::runtime_view_iterator; /*! @brief Default constructor to use to create empty, invalid views. */ basic_runtime_view() noexcept : basic_runtime_view{allocator_type{}} {} /** * @brief Constructs an empty, invalid view with a given allocator. * @param allocator The allocator to use. */ explicit basic_runtime_view(const allocator_type &allocator) : pools{allocator}, filter{allocator} {} /*! @brief Default copy constructor. */ basic_runtime_view(const basic_runtime_view &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) : pools{other.pools, allocator}, filter{other.filter, allocator} {} /*! @brief Default move constructor. */ basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) : pools{std::move(other.pools), allocator}, filter{std::move(other.filter), allocator} {} /** * @brief Default copy assignment operator. * @return This container. */ basic_runtime_view &operator=(const basic_runtime_view &) = default; /** * @brief Default move assignment operator. * @return This container. */ basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v) = default; /** * @brief Exchanges the contents with those of a given view. * @param other View to exchange the content with. */ void swap(basic_runtime_view &other) { using std::swap; swap(pools, other.pools); swap(filter, other.filter); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return pools.get_allocator(); } /*! @brief Clears the view. */ void clear() { pools.clear(); filter.clear(); } /** * @brief Appends an opaque storage object to a runtime view. * @param base An opaque reference to a storage object. * @return This runtime view. */ basic_runtime_view &iterate(base_type &base) { if(pools.empty() || !(base.size() < pools[0u]->size())) { pools.push_back(&base); } else { pools.push_back(std::exchange(pools[0u], &base)); } return *this; } /** * @brief Adds an opaque storage object as a filter of a runtime view. * @param base An opaque reference to a storage object. * @return This runtime view. */ basic_runtime_view &exclude(base_type &base) { filter.push_back(&base); return *this; } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const { return pools.empty() ? size_type{} : pools.front()->size(); } /** * @brief Returns an iterator to the first entity that has the given * components. * * The returned iterator points to the first entity that has the given * components. If the view is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity that has the given components. */ [[nodiscard]] iterator begin() const { return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->begin()}; } /** * @brief Returns an iterator that is past the last entity that has the * given components. * * The returned iterator points to the entity following the last entity that * has the given components. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity that has the * given components. */ [[nodiscard]] iterator end() const { return pools.empty() ? iterator{} : iterator{pools, filter, pools[0]->end()}; } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const { return !pools.empty() && std::all_of(pools.cbegin(), pools.cend(), [entt](const auto *curr) { return curr->contains(entt); }) && std::none_of(filter.cbegin(), filter.cend(), [entt](const auto *curr) { return curr && curr->contains(entt); }); } /** * @brief Iterates entities and applies the given function object to them. * * The function object is invoked for each entity. It is provided only with * the entity itself. To get the components, users can use the registry with * which the view was built.
* The signature of the function should be equivalent to the following: * * @code{.cpp} * void(const entity_type); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { for(const auto entity: *this) { func(entity); } } private: container_type pools; container_type filter; }; } // namespace entt #endif // #include "entity/snapshot.hpp" #ifndef ENTT_ENTITY_SNAPSHOT_HPP #define ENTT_ENTITY_SNAPSHOT_HPP #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "view.hpp" namespace entt { /** * @brief Utility class to create snapshots from a registry. * * A _snapshot_ can be either a dump of the entire registry or a narrower * selection of components of interest.
* This type can be used in both cases if provided with a correctly configured * output archive. * * @tparam Registry Basic registry type. */ template class basic_snapshot { using entity_traits = entt_traits; template void get(Archive &archive, std::size_t sz, It first, It last) const { const auto view = reg->template view(); archive(typename entity_traits::entity_type(sz)); while(first != last) { const auto entt = *(first++); if(reg->template all_of(entt)) { std::apply(archive, std::tuple_cat(std::make_tuple(entt), view.get(entt))); } } } template void component(Archive &archive, It first, It last, std::index_sequence) const { std::array size{}; auto begin = first; while(begin != last) { const auto entt = *(begin++); ((reg->template all_of(entt) ? ++size[Index] : 0u), ...); } (get(archive, size[Index], first, last), ...); } public: /*! Basic registry type. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_snapshot(const registry_type &source) noexcept : reg{&source} {} /*! @brief Default move constructor. */ basic_snapshot(basic_snapshot &&) noexcept = default; /*! @brief Default move assignment operator. @return This snapshot. */ basic_snapshot &operator=(basic_snapshot &&) noexcept = default; /** * @brief Puts aside all the entities from the underlying registry. * * Entities are serialized along with their versions. Destroyed entities are * taken in consideration as well by this function. * * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot &entities(Archive &archive) const { const auto sz = reg->size(); archive(typename entity_traits::entity_type(sz + 1u)); archive(reg->released()); for(auto first = reg->data(), last = first + sz; first != last; ++first) { archive(*first); } return *this; } /** * @brief Puts aside the given components. * * Each instance is serialized together with the entity to which it belongs. * Entities are serialized along with their versions. * * @tparam Component Types of components to serialize. * @tparam Archive Type of output archive. * @param archive A valid reference to an output archive. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot &component(Archive &archive) const { if constexpr(sizeof...(Component) == 1u) { const auto view = reg->template view(); (component(archive, view.rbegin(), view.rend()), ...); return *this; } else { (component(archive), ...); return *this; } } /** * @brief Puts aside the given components for the entities in a range. * * Each instance is serialized together with the entity to which it belongs. * Entities are serialized along with their versions. * * @tparam Component Types of components to serialize. * @tparam Archive Type of output archive. * @tparam It Type of input iterator. * @param archive A valid reference to an output archive. * @param first An iterator to the first element of the range to serialize. * @param last An iterator past the last element of the range to serialize. * @return An object of this type to continue creating the snapshot. */ template const basic_snapshot &component(Archive &archive, It first, It last) const { component(archive, first, last, std::index_sequence_for{}); return *this; } private: const registry_type *reg; }; /** * @brief Utility class to restore a snapshot as a whole. * * A snapshot loader requires that the destination registry be empty and loads * all the data at once while keeping intact the identifiers that the entities * originally had.
* An example of use is the implementation of a save/restore utility. * * @tparam Registry Basic registry type. */ template class basic_snapshot_loader { using entity_traits = entt_traits; template void assign(Archive &archive) const { typename entity_traits::entity_type length{}; entity_type entt; archive(length); if constexpr(ignore_as_empty_v) { while(length--) { archive(entt); const auto entity = reg->valid(entt) ? entt : reg->create(entt); ENTT_ASSERT(entity == entt, "Entity not available for use"); reg->template emplace(entt); } } else { Component instance; while(length--) { archive(entt, instance); const auto entity = reg->valid(entt) ? entt : reg->create(entt); ENTT_ASSERT(entity == entt, "Entity not available for use"); reg->template emplace(entt, std::move(instance)); } } } public: /*! Basic registry type. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_snapshot_loader(registry_type &source) noexcept : reg{&source} { // restoring a snapshot as a whole requires a clean registry ENTT_ASSERT(reg->empty(), "Registry must be empty"); } /*! @brief Default move constructor. */ basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; /*! @brief Default move assignment operator. @return This loader. */ basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; /** * @brief Restores entities that were in use during serialization. * * This function restores the entities that were in use during serialization * and gives them the versions they originally had. * * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template const basic_snapshot_loader &entities(Archive &archive) const { typename entity_traits::entity_type length{}; archive(length); std::vector all(length); for(std::size_t pos{}; pos < length; ++pos) { archive(all[pos]); } reg->assign(++all.cbegin(), all.cend(), all[0u]); return *this; } /** * @brief Restores components and assigns them to the right entities. * * The template parameter list must be exactly the same used during * serialization. In the event that the entity to which the component is * assigned doesn't exist yet, the loader will take care to create it with * the version it originally had. * * @tparam Component Types of components to restore. * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A valid loader to continue restoring data. */ template const basic_snapshot_loader &component(Archive &archive) const { (assign(archive), ...); return *this; } /** * @brief Destroys those entities that have no components. * * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
* This functions helps to identify and destroy those entities. * * @return A valid loader to continue restoring data. */ const basic_snapshot_loader &orphans() const { reg->each([this](const auto entt) { if(reg->orphan(entt)) { reg->release(entt); } }); return *this; } private: registry_type *reg; }; /** * @brief Utility class for _continuous loading_. * * A _continuous loader_ is designed to load data from a source registry to a * (possibly) non-empty destination. The loader can accommodate in a registry * more than one snapshot in a sort of _continuous loading_ that updates the * destination one step at a time.
* Identifiers that entities originally had are not transferred to the target. * Instead, the loader maps remote identifiers to local ones while restoring a * snapshot.
* An example of use is the implementation of a client-server applications with * the requirement of transferring somehow parts of the representation side to * side. * * @tparam Registry Basic registry type. */ template class basic_continuous_loader { using entity_traits = entt_traits; void destroy(typename Registry::entity_type entt) { if(const auto it = remloc.find(entt); it == remloc.cend()) { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); reg->destroy(local); } } void restore(typename Registry::entity_type entt) { const auto it = remloc.find(entt); if(it == remloc.cend()) { const auto local = reg->create(); remloc.emplace(entt, std::make_pair(local, true)); } else { if(!reg->valid(remloc[entt].first)) { remloc[entt].first = reg->create(); } // set the dirty flag remloc[entt].second = true; } } template auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) { // map like container Container other; for(auto &&pair: container) { using first_type = std::remove_const_t::first_type>; using second_type = typename std::decay_t::second_type; if constexpr(std::is_same_v && std::is_same_v) { other.emplace(map(pair.first), map(pair.second)); } else if constexpr(std::is_same_v) { other.emplace(map(pair.first), std::move(pair.second)); } else { static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); other.emplace(std::move(pair.first), map(pair.second)); } } using std::swap; swap(container, other); } template auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { // vector like container static_assert(std::is_same_v, "Invalid value type"); for(auto &&entt: container) { entt = map(entt); } } template void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) { if constexpr(!std::is_same_v) { return; } else if constexpr(std::is_same_v) { instance.*member = map(instance.*member); } else { // maybe a container? let's try... update(0, instance.*member); } } template void remove_if_exists() { for(auto &&ref: remloc) { const auto local = ref.second.first; if(reg->valid(local)) { reg->template remove(local); } } } template void assign(Archive &archive, [[maybe_unused]] Member Other::*...member) { typename entity_traits::entity_type length{}; entity_type entt; archive(length); if constexpr(ignore_as_empty_v) { while(length--) { archive(entt); restore(entt); reg->template emplace_or_replace(map(entt)); } } else { Component instance; while(length--) { archive(entt, instance); (update(instance, member), ...); restore(entt); reg->template emplace_or_replace(map(entt), std::move(instance)); } } } public: /*! Basic registry type. */ using registry_type = Registry; /*! @brief Underlying entity identifier. */ using entity_type = typename registry_type::entity_type; /** * @brief Constructs an instance that is bound to a given registry. * @param source A valid reference to a registry. */ basic_continuous_loader(registry_type &source) noexcept : reg{&source} {} /*! @brief Default move constructor. */ basic_continuous_loader(basic_continuous_loader &&) = default; /*! @brief Default move assignment operator. @return This loader. */ basic_continuous_loader &operator=(basic_continuous_loader &&) = default; /** * @brief Restores entities that were in use during serialization. * * This function restores the entities that were in use during serialization * and creates local counterparts for them if required. * * @tparam Archive Type of input archive. * @param archive A valid reference to an input archive. * @return A non-const reference to this loader. */ template basic_continuous_loader &entities(Archive &archive) { typename entity_traits::entity_type length{}; entity_type entt{}; archive(length); // discards the head of the list of destroyed entities archive(entt); for(std::size_t pos{}, last = length - 1u; pos < last; ++pos) { archive(entt); if(const auto entity = entity_traits::to_entity(entt); entity == pos) { restore(entt); } else { destroy(entt); } } return *this; } /** * @brief Restores components and assigns them to the right entities. * * The template parameter list must be exactly the same used during * serialization. In the event that the entity to which the component is * assigned doesn't exist yet, the loader will take care to create a local * counterpart for it.
* Members can be either data members of type entity_type or containers of * entities. In both cases, the loader will visit them and update the * entities by replacing each one with its local counterpart. * * @tparam Component Type of component to restore. * @tparam Archive Type of input archive. * @tparam Other Types of components to update with local counterparts. * @tparam Member Types of members to update with their local counterparts. * @param archive A valid reference to an input archive. * @param member Members to update with their local counterparts. * @return A non-const reference to this loader. */ template basic_continuous_loader &component(Archive &archive, Member Other::*...member) { (remove_if_exists(), ...); (assign(archive, member...), ...); return *this; } /** * @brief Helps to purge entities that no longer have a conterpart. * * Users should invoke this member function after restoring each snapshot, * unless they know exactly what they are doing. * * @return A non-const reference to this loader. */ basic_continuous_loader &shrink() { auto it = remloc.begin(); while(it != remloc.cend()) { const auto local = it->second.first; bool &dirty = it->second.second; if(dirty) { dirty = false; ++it; } else { if(reg->valid(local)) { reg->destroy(local); } it = remloc.erase(it); } } return *this; } /** * @brief Destroys those entities that have no components. * * In case all the entities were serialized but only part of the components * was saved, it could happen that some of the entities have no components * once restored.
* This functions helps to identify and destroy those entities. * * @return A non-const reference to this loader. */ basic_continuous_loader &orphans() { reg->each([this](const auto entt) { if(reg->orphan(entt)) { reg->release(entt); } }); return *this; } /** * @brief Tests if a loader knows about a given entity. * @param entt A valid identifier. * @return True if `entity` is managed by the loader, false otherwise. */ [[nodiscard]] bool contains(entity_type entt) const noexcept { return (remloc.find(entt) != remloc.cend()); } /** * @brief Returns the identifier to which an entity refers. * @param entt A valid identifier. * @return The local identifier if any, the null entity otherwise. */ [[nodiscard]] entity_type map(entity_type entt) const noexcept { const auto it = remloc.find(entt); entity_type other = null; if(it != remloc.cend()) { other = it->second.first; } return other; } private: dense_map> remloc; registry_type *reg; }; } // namespace entt #endif // #include "entity/sparse_set.hpp" #ifndef ENTT_ENTITY_SPARSE_SET_HPP #define ENTT_ENTITY_SPARSE_SET_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/algorithm.hpp" // #include "../core/any.hpp" // #include "../core/memory.hpp" // #include "../core/type_info.hpp" // #include "entity.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct sparse_set_iterator final { using value_type = typename Container::value_type; using pointer = typename Container::const_pointer; using reference = typename Container::const_reference; using difference_type = typename Container::difference_type; using iterator_category = std::random_access_iterator_tag; constexpr sparse_set_iterator() noexcept : packed{}, offset{} {} constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept : packed{std::addressof(ref)}, offset{idx} {} constexpr sparse_set_iterator &operator++() noexcept { return --offset, *this; } constexpr sparse_set_iterator operator++(int) noexcept { sparse_set_iterator orig = *this; return ++(*this), orig; } constexpr sparse_set_iterator &operator--() noexcept { return ++offset, *this; } constexpr sparse_set_iterator operator--(int) noexcept { sparse_set_iterator orig = *this; return operator--(), orig; } constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { sparse_set_iterator copy = *this; return (copy += value); } constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return packed->data()[index() - value]; } [[nodiscard]] constexpr pointer operator->() const noexcept { return packed->data() + index(); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } private: const Container *packed; difference_type offset; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template [[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template [[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template [[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { return !(lhs < rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @brief Sparse set deletion policy. */ enum class deletion_policy : std::uint8_t { /*! @brief Swap-and-pop deletion policy. */ swap_and_pop = 0u, /*! @brief In-place deletion policy. */ in_place = 1u }; /** * @brief Basic sparse set implementation. * * Sparse set or packed array or whatever is the name users give it.
* Two arrays: an _external_ one and an _internal_ one; a _sparse_ one and a * _packed_ one; one used for direct access through contiguous memory, the other * one used to get the data through an extra level of indirection.
* This is largely used by the registry to offer users the fastest access ever * to the components. Views and groups in general are almost entirely designed * around sparse sets. * * This type of data structure is widely documented in the literature and on the * web. This is nothing more than a customized implementation suitable for the * purpose of the framework. * * @note * Internal data structures arrange elements to maximize performance. There are * no guarantees that entities are returned in the insertion order when iterate * a sparse set. Do not make assumption on the order in any case. * * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_sparse_set { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector; using entity_traits = entt_traits; [[nodiscard]] auto sparse_ptr(const Entity entt) const { const auto pos = static_cast(entity_traits::to_entity(entt)); const auto page = pos / entity_traits::page_size; return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, entity_traits::page_size)) : nullptr; } [[nodiscard]] auto &sparse_ref(const Entity entt) const { ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); const auto pos = static_cast(entity_traits::to_entity(entt)); return sparse[pos / entity_traits::page_size][fast_mod(pos, entity_traits::page_size)]; } [[nodiscard]] auto &assure_at_least(const Entity entt) { const auto pos = static_cast(entity_traits::to_entity(entt)); const auto page = pos / entity_traits::page_size; if(!(page < sparse.size())) { sparse.resize(page + 1u, nullptr); } if(!sparse[page]) { auto page_allocator{packed.get_allocator()}; sparse[page] = alloc_traits::allocate(page_allocator, entity_traits::page_size); std::uninitialized_fill(sparse[page], sparse[page] + entity_traits::page_size, null); } auto &elem = sparse[page][fast_mod(pos, entity_traits::page_size)]; ENTT_ASSERT(elem == null, "Slot not available"); return elem; } void release_sparse_pages() { auto page_allocator{packed.get_allocator()}; for(auto &&page: sparse) { if(page != nullptr) { std::destroy(page, page + entity_traits::page_size); alloc_traits::deallocate(page_allocator, page, entity_traits::page_size); page = nullptr; } } } private: virtual const void *get_at(const std::size_t) const { return nullptr; } virtual void swap_at(const std::size_t, const std::size_t) {} virtual void move_element(const std::size_t, const std::size_t) {} protected: /*! @brief Random access iterator type. */ using basic_iterator = internal::sparse_set_iterator; /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. */ void swap_and_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatched"); auto &self = sparse_ref(*it); const auto entt = entity_traits::to_entity(self); sparse_ref(packed.back()) = entity_traits::combine(entt, entity_traits::to_integral(packed.back())); packed[static_cast(entt)] = packed.back(); // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((packed.back() = null, true), ""); // lazy self-assignment guard self = null; packed.pop_back(); } /** * @brief Erases an entity from a sparse set. * @param it An iterator to the element to pop. */ void in_place_pop(const basic_iterator it) { ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatched"); const auto entt = entity_traits::to_entity(std::exchange(sparse_ref(*it), null)); packed[static_cast(entt)] = std::exchange(free_list, entity_traits::combine(entt, entity_traits::reserved)); } protected: /** * @brief Erases entities from a sparse set. * @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. */ virtual void pop(basic_iterator first, basic_iterator last) { if(mode == deletion_policy::swap_and_pop) { for(; first != last; ++first) { swap_and_pop(first); } } else { for(; first != last; ++first) { in_place_pop(first); } } } /** * @brief Assigns an entity to a sparse set. * @param entt A valid identifier. * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ virtual basic_iterator try_emplace(const Entity entt, const bool force_back, const void * = nullptr) { ENTT_ASSERT(!contains(entt), "Set already contains entity"); if(auto &elem = assure_at_least(entt); free_list == null || force_back) { packed.push_back(entt); elem = entity_traits::combine(static_cast(packed.size() - 1u), entity_traits::to_integral(entt)); return begin(); } else { const auto pos = static_cast(entity_traits::to_entity(free_list)); elem = entity_traits::combine(entity_traits::to_integral(free_list), entity_traits::to_integral(entt)); free_list = std::exchange(packed[pos], entt); return --(end() - pos); } } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Underlying entity identifier. */ using entity_type = typename entity_traits::value_type; /*! @brief Underlying version type. */ using version_type = typename entity_traits::version_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained entities. */ using pointer = typename packed_container_type::const_pointer; /*! @brief Random access iterator type. */ using iterator = basic_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = reverse_iterator; /*! @brief Default constructor. */ basic_sparse_set() : basic_sparse_set{type_id()} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit basic_sparse_set(const allocator_type &allocator) : basic_sparse_set{type_id(), deletion_policy::swap_and_pop, allocator} {} /** * @brief Constructs an empty container with the given policy and allocator. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ explicit basic_sparse_set(deletion_policy pol, const allocator_type &allocator = {}) : basic_sparse_set{type_id(), pol, allocator} {} /** * @brief Constructs an empty container with the given value type, policy * and allocator. * @param value Returned value type, if any. * @param pol Type of deletion policy. * @param allocator The allocator to use (possibly default-constructed). */ explicit basic_sparse_set(const type_info &value, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) : sparse{allocator}, packed{allocator}, info{&value}, free_list{tombstone}, mode{pol} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_sparse_set(basic_sparse_set &&other) noexcept : sparse{std::move(other.sparse)}, packed{std::move(other.packed)}, info{other.info}, free_list{std::exchange(other.free_list, tombstone)}, mode{other.mode} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept : sparse{std::move(other.sparse), allocator}, packed{std::move(other.packed), allocator}, info{other.info}, free_list{std::exchange(other.free_list, tombstone)}, mode{other.mode} { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); } /*! @brief Default destructor. */ virtual ~basic_sparse_set() { release_sparse_pages(); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This sparse set. */ basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); release_sparse_pages(); sparse = std::move(other.sparse); packed = std::move(other.packed); info = other.info; free_list = std::exchange(other.free_list, tombstone); mode = other.mode; return *this; } /** * @brief Exchanges the contents with those of a given sparse set. * @param other Sparse set to exchange the content with. */ void swap(basic_sparse_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(info, other.info); swap(free_list, other.free_list); swap(mode, other.mode); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return packed.get_allocator(); } /** * @brief Returns the deletion policy of a sparse set. * @return The deletion policy of the sparse set. */ [[nodiscard]] deletion_policy policy() const noexcept { return mode; } /** * @brief Increases the capacity of a sparse set. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ virtual void reserve(const size_type cap) { packed.reserve(cap); } /** * @brief Returns the number of elements that a sparse set has currently * allocated space for. * @return Capacity of the sparse set. */ [[nodiscard]] virtual size_type capacity() const noexcept { return packed.capacity(); } /*! @brief Requests the removal of unused capacity. */ virtual void shrink_to_fit() { packed.shrink_to_fit(); } /** * @brief Returns the extent of a sparse set. * * The extent of a sparse set is also the size of the internal sparse array. * There is no guarantee that the internal packed array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Extent of the sparse set. */ [[nodiscard]] size_type extent() const noexcept { return sparse.size() * entity_traits::page_size; } /** * @brief Returns the number of elements in a sparse set. * * The number of elements is also the size of the internal packed array. * There is no guarantee that the internal sparse array has the same size. * Usually the size of the internal sparse array is equal or greater than * the one of the internal packed array. * * @return Number of elements. */ [[nodiscard]] size_type size() const noexcept { return packed.size(); } /** * @brief Checks whether a sparse set is empty. * @return True if the sparse set is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.empty(); } /** * @brief Direct access to the internal packed array. * @return A pointer to the internal packed array. */ [[nodiscard]] pointer data() const noexcept { return packed.data(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first entity of the internal packed * array. If the sparse set is empty, the returned iterator will be equal to * `end()`. * * @return An iterator to the first entity of the sparse set. */ [[nodiscard]] const_iterator begin() const noexcept { const auto pos = static_cast(packed.size()); return iterator{packed, pos}; } /*! @copydoc begin */ [[nodiscard]] const_iterator cbegin() const noexcept { return begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last entity in * a sparse set. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the element following the last entity of a sparse * set. */ [[nodiscard]] iterator end() const noexcept { return iterator{packed, {}}; } /*! @copydoc end */ [[nodiscard]] const_iterator cend() const noexcept { return end(); } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first entity of the reversed internal * packed array. If the sparse set is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first entity of the reversed internal packed * array. */ [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return std::make_reverse_iterator(end()); } /*! @copydoc rbegin */ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return rbegin(); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last entity in * the reversed sparse set. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last entity of the * reversed sparse set. */ [[nodiscard]] reverse_iterator rend() const noexcept { return std::make_reverse_iterator(begin()); } /*! @copydoc rend */ [[nodiscard]] const_reverse_iterator crend() const noexcept { return rend(); } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? --(end() - index(entt)) : end(); } /** * @brief Checks if a sparse set contains an entity. * @param entt A valid identifier. * @return True if the sparse set contains the entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto cap = entity_traits::to_entity(null); // testing versions permits to avoid accessing the packed array return elem && (((~cap & entity_traits::to_integral(entt)) ^ entity_traits::to_integral(*elem)) < cap); } /** * @brief Returns the contained version for an identifier. * @param entt A valid identifier. * @return The version for the given identifier if present, the tombstone * version otherwise. */ [[nodiscard]] version_type current(const entity_type entt) const noexcept { const auto elem = sparse_ptr(entt); constexpr auto fallback = entity_traits::to_version(tombstone); return elem ? entity_traits::to_version(*elem) : fallback; } /** * @brief Returns the position of an entity in a sparse set. * * @warning * Attempting to get the position of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid identifier. * @return The position of the entity in the sparse set. */ [[nodiscard]] size_type index(const entity_type entt) const noexcept { ENTT_ASSERT(contains(entt), "Set does not contain entity"); return static_cast(entity_traits::to_entity(sparse_ref(entt))); } /** * @brief Returns the entity at specified location, with bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location if any, a null entity otherwise. */ [[nodiscard]] entity_type at(const size_type pos) const noexcept { return pos < packed.size() ? packed[pos] : null; } /** * @brief Returns the entity at specified location, without bounds checking. * @param pos The position for which to return the entity. * @return The entity at specified location. */ [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); return packed[pos]; } /** * @brief Returns the element assigned to an entity, if any. * * @warning * Attempting to use an entity that doesn't belong to the sparse set results * in undefined behavior. * * @param entt A valid identifier. * @return An opaque pointer to the element assigned to the entity, if any. */ [[nodiscard]] const void *get(const entity_type entt) const noexcept { return get_at(index(entt)); } /*! @copydoc get */ [[nodiscard]] void *get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Assigns an entity to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @param entt A valid identifier. * @param value Optional opaque value to forward to mixins, if any. * @return Iterator pointing to the emplaced element in case of success, the * `end()` iterator otherwise. */ iterator emplace(const entity_type entt, const void *value = nullptr) { return try_emplace(entt, false, value); } /** * @brief Bump the version number of an entity. * * @warning * Attempting to bump the version of an entity that doesn't belong to the * sparse set results in undefined behavior. * * @param entt A valid identifier. */ void bump(const entity_type entt) { auto &entity = sparse_ref(entt); ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); entity = entity_traits::combine(entity_traits::to_integral(entity), entity_traits::to_integral(entt)); packed[static_cast(entity_traits::to_entity(entity))] = entt; } /** * @brief Assigns one or more entities to a sparse set. * * @warning * Attempting to assign an entity that already belongs to the sparse set * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return Iterator pointing to the first element inserted in case of * success, the `end()` iterator otherwise. */ template iterator insert(It first, It last) { for(auto it = first; it != last; ++it) { try_emplace(*it, true); } return first == last ? end() : find(*first); } /** * @brief Erases an entity from a sparse set. * * @warning * Attempting to erase an entity that doesn't belong to the sparse set * results in undefined behavior. * * @param entt A valid identifier. */ void erase(const entity_type entt) { const auto it = --(end() - index(entt)); pop(it, it + 1u); } /** * @brief Erases entities from a set. * * @sa erase * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void erase(It first, It last) { if constexpr(std::is_same_v) { pop(first, last); } else { for(; first != last; ++first) { erase(*first); } } } /** * @brief Removes an entity from a sparse set if it exists. * @param entt A valid identifier. * @return True if the entity is actually removed, false otherwise. */ bool remove(const entity_type entt) { return contains(entt) && (erase(entt), true); } /** * @brief Removes entities from a sparse set if they exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @return The number of entities actually removed. */ template size_type remove(It first, It last) { size_type count{}; for(; first != last; ++first) { count += remove(*first); } return count; } /*! @brief Removes all tombstones from the packed array of a sparse set. */ void compact() { size_type from = packed.size(); for(; from && packed[from - 1u] == tombstone; --from) {} for(auto *it = &free_list; *it != null && from; it = std::addressof(packed[entity_traits::to_entity(*it)])) { if(const size_type to = entity_traits::to_entity(*it); to < from) { --from; move_element(from, to); using std::swap; swap(packed[from], packed[to]); const auto entity = static_cast(to); sparse_ref(packed[to]) = entity_traits::combine(entity, entity_traits::to_integral(packed[to])); *it = entity_traits::combine(static_cast(from), entity_traits::reserved); for(; from && packed[from - 1u] == tombstone; --from) {} } } free_list = tombstone; packed.resize(from); } /** * @brief Swaps two entities in a sparse set. * * For what it's worth, this function affects both the internal sparse array * and the internal packed array. Users should not care of that anyway. * * @warning * Attempting to swap entities that don't belong to the sparse set results * in undefined behavior. * * @param lhs A valid identifier. * @param rhs A valid identifier. */ void swap_elements(const entity_type lhs, const entity_type rhs) { ENTT_ASSERT(contains(lhs) && contains(rhs), "Set does not contain entities"); auto &entt = sparse_ref(lhs); auto &other = sparse_ref(rhs); const auto from = entity_traits::to_entity(entt); const auto to = entity_traits::to_entity(other); // basic no-leak guarantee (with invalid state) if swapping throws swap_at(static_cast(from), static_cast(to)); entt = entity_traits::combine(to, entity_traits::to_integral(packed[from])); other = entity_traits::combine(from, entity_traits::to_integral(packed[to])); using std::swap; swap(packed[from], packed[to]); } /** * @brief Sort the first count elements according to the given comparison * function. * * The comparison function object must return `true` if the first element * is _less_ than the second one, `false` otherwise. The signature of the * comparison function should be equivalent to the following: * * @code{.cpp} * bool(const Entity, const Entity); * @endcode * * Moreover, the comparison function object shall induce a * _strict weak ordering_ on the values. * * The sort function object must offer a member function template * `operator()` that accepts three arguments: * * * An iterator to the first element of the range to sort. * * An iterator past the last element of the range to sort. * * A comparison function to use to compare the elements. * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param length Number of elements to sort. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort_n(const size_type length, Compare compare, Sort algo = Sort{}, Args &&...args) { ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); ENTT_ASSERT(free_list == null, "Partial sorting with tombstones is not supported"); algo(packed.rend() - length, packed.rend(), std::move(compare), std::forward(args)...); for(size_type pos{}; pos < length; ++pos) { auto curr = pos; auto next = index(packed[curr]); while(curr != next) { const auto idx = index(packed[next]); const auto entt = packed[curr]; swap_at(next, idx); const auto entity = static_cast(curr); sparse_ref(entt) = entity_traits::combine(entity, entity_traits::to_integral(packed[curr])); curr = std::exchange(next, idx); } } } /** * @brief Sort all elements according to the given comparison function. * * @sa sort_n * * @tparam Compare Type of comparison function object. * @tparam Sort Type of sort function object. * @tparam Args Types of arguments to forward to the sort function object. * @param compare A valid comparison function object. * @param algo A valid sort function object. * @param args Arguments to forward to the sort function object, if any. */ template void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { compact(); sort_n(packed.size(), std::move(compare), std::move(algo), std::forward(args)...); } /** * @brief Sort entities according to their order in another sparse set. * * Entities that are part of both the sparse sets are ordered internally * according to the order they have in `other`. All the other entities goes * to the end of the list and there are no guarantees on their order.
* In other terms, this function can be used to impose the same order on two * sets by using one of them as a master and the other one as a slave. * * Iterating the sparse set with a couple of iterators returns elements in * the expected order after a call to `respect`. See `begin` and `end` for * more details. * * @param other The sparse sets that imposes the order of the entities. */ void respect(const basic_sparse_set &other) { compact(); const auto to = other.end(); auto from = other.begin(); for(size_type pos = packed.size() - 1; pos && from != to; ++from) { if(contains(*from)) { if(*from != packed[pos]) { // basic no-leak guarantee (with invalid state) if swapping throws swap_elements(packed[pos], *from); } --pos; } } } /*! @brief Clears a sparse set. */ void clear() { if(const auto last = end(); free_list == null) { pop(begin(), last); } else { for(auto &&entity: *this) { // tombstone filter on itself if(const auto it = find(entity); it != last) { pop(it, it + 1u); } } } free_list = tombstone; packed.clear(); } /** * @brief Returned value type, if any. * @return Returned value type, if any. */ const type_info &type() const noexcept { return *info; } /*! @brief Forwards variables to derived classes, if any. */ virtual void bind(any) noexcept {} private: sparse_container_type sparse; packed_container_type packed; const type_info *info; entity_type free_list; deletion_policy mode; }; } // namespace entt #endif // #include "entity/storage.hpp" #ifndef ENTT_ENTITY_STORAGE_HPP #define ENTT_ENTITY_STORAGE_HPP #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" // #include "../core/iterator.hpp" // #include "../core/memory.hpp" // #include "../core/type_info.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage_mixin.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class storage_iterator final { friend storage_iterator; using container_type = std::remove_const_t; using alloc_traits = std::allocator_traits; using comp_traits = component_traits>; using iterator_traits = std::iterator_traits, typename alloc_traits::template rebind_traits::element_type>::const_pointer, typename alloc_traits::template rebind_traits::element_type>::pointer>>; public: using value_type = typename iterator_traits::value_type; using pointer = typename iterator_traits::pointer; using reference = typename iterator_traits::reference; using difference_type = typename iterator_traits::difference_type; using iterator_category = std::random_access_iterator_tag; constexpr storage_iterator() noexcept = default; constexpr storage_iterator(Container *ref, const difference_type idx) noexcept : packed{ref}, offset{idx} {} template, typename = std::enable_if_t> constexpr storage_iterator(const storage_iterator> &other) noexcept : storage_iterator{other.packed, other.offset} {} constexpr storage_iterator &operator++() noexcept { return --offset, *this; } constexpr storage_iterator operator++(int) noexcept { storage_iterator orig = *this; return ++(*this), orig; } constexpr storage_iterator &operator--() noexcept { return ++offset, *this; } constexpr storage_iterator operator--(int) noexcept { storage_iterator orig = *this; return operator--(), orig; } constexpr storage_iterator &operator+=(const difference_type value) noexcept { offset -= value; return *this; } constexpr storage_iterator operator+(const difference_type value) const noexcept { storage_iterator copy = *this; return (copy += value); } constexpr storage_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr storage_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { const auto pos = index() - value; return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; } [[nodiscard]] constexpr pointer operator->() const noexcept { const auto pos = index(); return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr difference_type index() const noexcept { return offset - 1; } private: Container *packed; difference_type offset; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return rhs.index() - lhs.index(); } template [[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() > rhs.index(); } template [[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return lhs.index() < rhs.index(); } template [[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { return !(lhs < rhs); } template class extended_storage_iterator final { template friend class extended_storage_iterator; public: using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::forward_as_tuple(*std::declval()...))); using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr extended_storage_iterator() : it{} {} constexpr extended_storage_iterator(It base, Other... other) : it{base, other...} {} template && ...) && (std::is_constructible_v && ...)>> constexpr extended_storage_iterator(const extended_storage_iterator &other) : it{other.it} {} constexpr extended_storage_iterator &operator++() noexcept { return ++std::get(it), (++std::get(it), ...), *this; } constexpr extended_storage_iterator operator++(int) noexcept { extended_storage_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {*std::get(it), *std::get(it)...}; } template friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; private: std::tuple it; }; template [[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return std::get<0>(lhs.it) == std::get<0>(rhs.it); } template [[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic storage implementation. * * Internal data structures arrange elements to maximize performance. There are * no guarantees that objects are returned in the insertion order when iterate * a storage. Do not make assumption on the order in any case. * * @warning * Empty types aren't explicitly instantiated. Therefore, many of the functions * normally available for non-empty types will not be available for empty ones. * * @tparam Type Type of objects assigned to the entities. * @tparam Entity A valid entity type (see entt_traits for more details). * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_storage: public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using underlying_type = basic_sparse_set>; using container_type = std::vector>; using comp_traits = component_traits; static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); [[nodiscard]] auto &element_at(const std::size_t pos) const { return packed.first()[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; } auto assure_at_least(const std::size_t pos) { auto &&container = packed.first(); const auto idx = pos / comp_traits::page_size; if(!(idx < container.size())) { auto curr = container.size(); container.resize(idx + 1u, nullptr); ENTT_TRY { for(const auto last = container.size(); curr < last; ++curr) { container[curr] = alloc_traits::allocate(packed.second(), comp_traits::page_size); } } ENTT_CATCH { container.resize(curr); ENTT_THROW; } } return container[idx] + fast_mod(pos, comp_traits::page_size); } template auto emplace_element(const Entity entt, const bool force_back, Args &&...args) { const auto it = base_type::try_emplace(entt, force_back); ENTT_TRY { auto elem = assure_at_least(static_cast(it.index())); entt::uninitialized_construct_using_allocator(to_address(elem), packed.second(), std::forward(args)...); } ENTT_CATCH { base_type::pop(it, it + 1u); ENTT_THROW; } return it; } void shrink_to_size(const std::size_t sz) { for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { if constexpr(comp_traits::in_place_delete) { if(base_type::at(pos) != tombstone) { std::destroy_at(std::addressof(element_at(pos))); } } else { std::destroy_at(std::addressof(element_at(pos))); } } auto &&container = packed.first(); auto page_allocator{packed.second()}; const auto from = (sz + comp_traits::page_size - 1u) / comp_traits::page_size; for(auto pos = from, last = container.size(); pos < last; ++pos) { alloc_traits::deallocate(page_allocator, container[pos], comp_traits::page_size); } container.resize(from); } private: const void *get_at(const std::size_t pos) const final { return std::addressof(element_at(pos)); } void swap_at([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) final { // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy ENTT_ASSERT((lhs + 1u) && !is_pinned_type_v, "Pinned type"); if constexpr(!is_pinned_type_v) { using std::swap; swap(element_at(lhs), element_at(rhs)); } } void move_element([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) final { // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); if constexpr(!is_pinned_type_v) { auto &elem = element_at(from); entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), packed.second(), std::move(elem)); std::destroy_at(std::addressof(elem)); } } protected: /*! @brief Random access iterator type. */ using basic_iterator = typename underlying_type::basic_iterator; /** * @brief Erases entities from a sparse set. * @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. */ void pop(basic_iterator first, basic_iterator last) override { for(; first != last; ++first) { // cannot use first.index() because it would break with cross iterators auto &elem = element_at(base_type::index(*first)); if constexpr(comp_traits::in_place_delete) { base_type::in_place_pop(first); std::destroy_at(std::addressof(elem)); } else { auto &other = element_at(base_type::size() - 1u); // destroying on exit allows reentrant destructors [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); std::destroy_at(std::addressof(other)); base_type::swap_and_pop(first); } } } /** * @brief Assigns an entity to a storage. * @param entt A valid identifier. * @param value Optional opaque value. * @param force_back Force back insertion. * @return Iterator pointing to the emplaced element. */ basic_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { if(value) { if constexpr(std::is_copy_constructible_v) { return emplace_element(entt, force_back, *static_cast(value)); } else { return base_type::end(); } } else { if constexpr(std::is_default_constructible_v) { return emplace_element(entt, force_back); } else { return base_type::end(); } } } public: /*! @brief Base type. */ using base_type = underlying_type; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Pointer type to contained elements. */ using pointer = typename container_type::pointer; /*! @brief Constant pointer type to contained elements. */ using const_pointer = typename alloc_traits::template rebind_traits::const_pointer; /*! @brief Random access iterator type. */ using iterator = internal::storage_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::storage_iterator; /*! @brief Reverse iterator type. */ using reverse_iterator = std::reverse_iterator; /*! @brief Constant reverse iterator type. */ using const_reverse_iterator = std::reverse_iterator; /*! @brief Extended iterable storage proxy. */ using iterable = iterable_adaptor>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() : basic_storage{allocator_type{}} {} /** * @brief Constructs an empty storage with a given allocator. * @param allocator The allocator to use. */ explicit basic_storage(const allocator_type &allocator) : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator}, packed{container_type{allocator}, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage(basic_storage &&other) noexcept : base_type{std::move(other)}, packed{std::move(other.packed)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator}, packed{container_type{std::move(other.packed.first()), allocator}, allocator} { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); } /*! @brief Default destructor. */ ~basic_storage() override { shrink_to_size(0u); } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ basic_storage &operator=(basic_storage &&other) noexcept { ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.second() == other.packed.second(), "Copying a storage is not allowed"); shrink_to_size(0u); base_type::operator=(std::move(other)); packed.first() = std::move(other.packed.first()); propagate_on_container_move_assignment(packed.second(), other.packed.second()); return *this; } /** * @brief Exchanges the contents with those of a given storage. * @param other Storage to exchange the content with. */ void swap(basic_storage &other) { using std::swap; underlying_type::swap(other); propagate_on_container_swap(packed.second(), other.packed.second()); swap(packed.first(), other.packed.first()); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{packed.second()}; } /** * @brief Increases the capacity of a storage. * * If the new capacity is greater than the current capacity, new storage is * allocated, otherwise the method does nothing. * * @param cap Desired capacity. */ void reserve(const size_type cap) override { if(cap != 0u) { base_type::reserve(cap); assure_at_least(cap - 1u); } } /** * @brief Returns the number of elements that a storage has currently * allocated space for. * @return Capacity of the storage. */ [[nodiscard]] size_type capacity() const noexcept override { return packed.first().size() * comp_traits::page_size; } /*! @brief Requests the removal of unused capacity. */ void shrink_to_fit() override { base_type::shrink_to_fit(); shrink_to_size(base_type::size()); } /** * @brief Direct access to the array of objects. * @return A pointer to the array of objects. */ [[nodiscard]] const_pointer raw() const noexcept { return packed.first().data(); } /*! @copydoc raw */ [[nodiscard]] pointer raw() noexcept { return packed.first().data(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the storage is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { const auto pos = static_cast(base_type::size()); return const_iterator{&packed.first(), pos}; } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { const auto pos = static_cast(base_type::size()); return iterator{&packed.first(), pos}; } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return const_iterator{&packed.first(), {}}; } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return iterator{&packed.first(), {}}; } /** * @brief Returns a reverse iterator to the beginning. * * The returned iterator points to the first instance of the reversed * internal array. If the storage is empty, the returned iterator will be * equal to `rend()`. * * @return An iterator to the first instance of the reversed internal array. */ [[nodiscard]] const_reverse_iterator crbegin() const noexcept { return std::make_reverse_iterator(cend()); } /*! @copydoc crbegin */ [[nodiscard]] const_reverse_iterator rbegin() const noexcept { return crbegin(); } /*! @copydoc rbegin */ [[nodiscard]] reverse_iterator rbegin() noexcept { return std::make_reverse_iterator(end()); } /** * @brief Returns a reverse iterator to the end. * * The returned iterator points to the element following the last instance * of the reversed internal array. Attempting to dereference the returned * iterator results in undefined behavior. * * @return An iterator to the element following the last instance of the * reversed internal array. */ [[nodiscard]] const_reverse_iterator crend() const noexcept { return std::make_reverse_iterator(cbegin()); } /*! @copydoc crend */ [[nodiscard]] const_reverse_iterator rend() const noexcept { return crend(); } /*! @copydoc rend */ [[nodiscard]] reverse_iterator rend() noexcept { return std::make_reverse_iterator(begin()); } /** * @brief Returns the object assigned to an entity. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. * @return The object assigned to the entity. */ [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { return element_at(base_type::index(entt)); } /*! @copydoc get */ [[nodiscard]] value_type &get(const entity_type entt) noexcept { return const_cast(std::as_const(*this).get(entt)); } /** * @brief Returns the object assigned to an entity as a tuple. * @param entt A valid identifier. * @return The object assigned to the entity as a tuple. */ [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { return std::forward_as_tuple(get(entt)); } /*! @copydoc get_as_tuple */ [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { return std::forward_as_tuple(get(entt)); } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. * @param args Parameters to use to construct an object for the entity. * @return A reference to the newly created object. */ template value_type &emplace(const entity_type entt, Args &&...args) { if constexpr(std::is_aggregate_v) { const auto it = emplace_element(entt, false, Type{std::forward(args)...}); return element_at(static_cast(it.index())); } else { const auto it = emplace_element(entt, false, std::forward(args)...); return element_at(static_cast(it.index())); } } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the updated instance. */ template value_type &patch(const entity_type entt, Func &&...func) { const auto idx = base_type::index(entt); auto &elem = element_at(idx); (std::forward(func)(elem), ...); return elem; } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given instance. * * @warning * Attempting to assign an entity that already belongs to the storage * results in undefined behavior. * * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param value An instance of the object to construct. */ template void insert(It first, It last, const value_type &value = {}) { for(; first != last; ++first) { emplace_element(*first, true, value); } } /** * @brief Assigns one or more entities to a storage and constructs their * objects from a given range. * * @sa construct * * @tparam EIt Type of input iterator. * @tparam CIt Type of input iterator. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param from An iterator to the first element of the range of objects. */ template::value_type, value_type>>> void insert(EIt first, EIt last, CIt from) { for(; first != last; ++first, ++from) { emplace_element(*first, true, *from); } } /** * @brief Returns an iterable object to use to _visit_ a storage. * * The iterable object returns a tuple that contains the current entity and * a reference to its component. * * @return An iterable object to use to _visit_ the storage. */ [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; } /*! @copydoc each */ [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; } private: compressed_pair packed; }; /*! @copydoc basic_storage */ template class basic_storage>> : public basic_sparse_set::template rebind_alloc> { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using underlying_type = basic_sparse_set>; using comp_traits = component_traits; public: /*! @brief Base type. */ using base_type = underlying_type; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Type of the objects assigned to entities. */ using value_type = Type; /*! @brief Underlying entity identifier. */ using entity_type = Entity; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Extended iterable storage proxy. */ using iterable = iterable_adaptor>; /*! @brief Constant extended iterable storage proxy. */ using const_iterable = iterable_adaptor>; /*! @brief Default constructor. */ basic_storage() : basic_storage{allocator_type{}} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit basic_storage(const allocator_type &allocator) : base_type{type_id(), deletion_policy{comp_traits::in_place_delete}, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_storage(basic_storage &&other) noexcept = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept : base_type{std::move(other), allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ basic_storage &operator=(basic_storage &&other) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{base_type::get_allocator()}; } /** * @brief Returns the object assigned to an entity, that is `void`. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. */ void get([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); } /** * @brief Returns an empty tuple. * * @warning * Attempting to use an entity that doesn't belong to the storage results in * undefined behavior. * * @param entt A valid identifier. * @return Returns an empty tuple. */ [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); return std::tuple{}; } /** * @brief Assigns an entity to a storage and constructs its object. * * @warning * Attempting to use an entity that already belongs to the storage results * in undefined behavior. * * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. */ template void emplace(const entity_type entt, Args &&...) { base_type::try_emplace(entt, false); } /** * @brief Updates the instance assigned to a given entity in-place. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. */ template void patch([[maybe_unused]] const entity_type entt, Func &&...func) { ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); (std::forward(func)(), ...); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of optional arguments. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. */ template void insert(It first, It last, Args &&...) { for(; first != last; ++first) { base_type::try_emplace(*first, true); } } /** * @brief Returns an iterable object to use to _visit_ a storage. * * The iterable object returns a tuple that contains the current entity. * * @return An iterable object to use to _visit_ the storage. */ [[nodiscard]] iterable each() noexcept { return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; } /*! @copydoc each */ [[nodiscard]] const_iterable each() const noexcept { return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; } }; } // namespace entt #endif // #include "entity/storage_mixin.hpp" #ifndef ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP #define ENTT_ENTITY_SIGH_STORAGE_MIXIN_HPP #include // #include "../config/config.h" // #include "../core/any.hpp" // #include "../signal/sigh.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Mixin type used to add signal support to storage types. * * The function type of a listener is equivalent to: * * @code{.cpp} * void(basic_registry &, entity_type); * @endcode * * This applies to all signals made available. * * @tparam Type The type of the underlying storage. */ template class sigh_storage_mixin final: public Type { using basic_registry_type = basic_registry; using sigh_type = sigh; using basic_iterator = typename Type::basic_iterator; void pop(basic_iterator first, basic_iterator last) override { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); for(; first != last; ++first) { const auto entt = *first; destruction.publish(*owner, entt); const auto it = Type::find(entt); Type::pop(it, it + 1u); } } basic_iterator try_emplace(const typename basic_registry_type::entity_type entt, const bool force_back, const void *value) final { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::try_emplace(entt, force_back, value); construction.publish(*owner, entt); return Type::find(entt); } public: /*! @brief Allocator type. */ using allocator_type = typename Type::allocator_type; /*! @brief Underlying entity identifier. */ using entity_type = typename Type::entity_type; /*! @brief Expected registry type. */ using registry_type = basic_registry_type; /*! @brief Default constructor. */ sigh_storage_mixin() : sigh_storage_mixin{allocator_type{}} {} /** * @brief Constructs an empty storage with a given allocator. * @param allocator The allocator to use. */ explicit sigh_storage_mixin(const allocator_type &allocator) : Type{allocator}, owner{}, construction{allocator}, destruction{allocator}, update{allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ sigh_storage_mixin(sigh_storage_mixin &&other) noexcept : Type{std::move(other)}, owner{other.owner}, construction{std::move(other.construction)}, destruction{std::move(other.destruction)}, update{std::move(other.update)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ sigh_storage_mixin(sigh_storage_mixin &&other, const allocator_type &allocator) noexcept : Type{std::move(other), allocator}, owner{other.owner}, construction{std::move(other.construction), allocator}, destruction{std::move(other.destruction), allocator}, update{std::move(other.update), allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This storage. */ sigh_storage_mixin &operator=(sigh_storage_mixin &&other) noexcept { Type::operator=(std::move(other)); owner = other.owner; construction = std::move(other.construction); destruction = std::move(other.destruction); update = std::move(other.update); return *this; } /** * @brief Exchanges the contents with those of a given storage. * @param other Storage to exchange the content with. */ void swap(sigh_storage_mixin &other) { using std::swap; Type::swap(other); swap(owner, other.owner); swap(construction, other.construction); swap(destruction, other.destruction); swap(update, other.update); } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever a new instance is created and assigned to an entity.
* Listeners are invoked after the object has been assigned to the entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_construct() noexcept { return sink{construction}; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is explicitly updated.
* Listeners are invoked after the object has been updated. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_update() noexcept { return sink{update}; } /** * @brief Returns a sink object. * * The sink returned by this function can be used to receive notifications * whenever an instance is removed from an entity and thus destroyed.
* Listeners are invoked before the object has been removed from the entity. * * @sa sink * * @return A temporary sink object. */ [[nodiscard]] auto on_destroy() noexcept { return sink{destruction}; } /** * @brief Assigns entities to a storage. * @tparam Args Types of arguments to use to construct the object. * @param entt A valid identifier. * @param args Parameters to use to initialize the object. * @return A reference to the newly created object. */ template decltype(auto) emplace(const entity_type entt, Args &&...args) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::emplace(entt, std::forward(args)...); construction.publish(*owner, entt); return this->get(entt); } /** * @brief Patches the given instance for an entity. * @tparam Func Types of the function objects to invoke. * @param entt A valid identifier. * @param func Valid function objects. * @return A reference to the patched instance. */ template decltype(auto) patch(const entity_type entt, Func &&...func) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::patch(entt, std::forward(func)...); update.publish(*owner, entt); return this->get(entt); } /** * @brief Assigns entities to a storage. * @tparam It Type of input iterator. * @tparam Args Types of arguments to use to construct the objects assigned * to the entities. * @param first An iterator to the first element of the range of entities. * @param last An iterator past the last element of the range of entities. * @param args Parameters to use to initialize the objects assigned to the * entities. */ template void insert(It first, It last, Args &&...args) { ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); Type::insert(first, last, std::forward(args)...); for(auto it = construction.empty() ? last : first; it != last; ++it) { construction.publish(*owner, *it); } } /** * @brief Forwards variables to derived classes, if any. * @param value A variable wrapped in an opaque container. */ void bind(any value) noexcept final { auto *reg = any_cast(&value); owner = reg ? reg : owner; Type::bind(std::move(value)); } private: basic_registry_type *owner; sigh_type construction; sigh_type destruction; sigh_type update; }; } // namespace entt #endif // #include "entity/view.hpp" #ifndef ENTT_ENTITY_VIEW_HPP #define ENTT_ENTITY_VIEW_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" // #include "../core/type_traits.hpp" // #include "component.hpp" // #include "entity.hpp" // #include "fwd.hpp" // #include "sparse_set.hpp" // #include "storage.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class view_iterator final { using iterator_type = typename Type::const_iterator; [[nodiscard]] bool valid() const noexcept { return ((Get != 0u) || (*it != tombstone)) && std::apply([entt = *it](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt = *it](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } public: using value_type = typename iterator_type::value_type; using pointer = typename iterator_type::pointer; using reference = typename iterator_type::reference; using difference_type = typename iterator_type::difference_type; using iterator_category = std::forward_iterator_tag; constexpr view_iterator() noexcept : it{}, last{}, pools{}, filter{} {} view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) noexcept : it{curr}, last{to}, pools{all_of}, filter{none_of} { while(it != last && !valid()) { ++it; } } view_iterator &operator++() noexcept { while(++it != last && !valid()) {} return *this; } view_iterator operator++(int) noexcept { view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] pointer operator->() const noexcept { return &*it; } [[nodiscard]] reference operator*() const noexcept { return *operator->(); } template friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; private: iterator_type it; iterator_type last; std::array pools; std::array filter; }; template [[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { return !(lhs == rhs); } template struct extended_view_iterator final { using difference_type = std::ptrdiff_t; using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})...)); using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr extended_view_iterator() : it{}, pools{} {} extended_view_iterator(It from, std::tuple storage) : it{from}, pools{storage} {} extended_view_iterator &operator++() noexcept { return ++it, *this; } extended_view_iterator operator++(int) noexcept { extended_view_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const noexcept { return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); } [[nodiscard]] pointer operator->() const noexcept { return operator*(); } template friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; private: It it; std::tuple pools; }; template [[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief View implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error, but for a few reasonable cases. */ template class basic_view; /** * @brief Multi component view. * * Multi component views iterate over those entities that are at least in the * given storage. During initialization, a multi component view looks at the * number of entities available for each component and uses the smallest set in * order to get a performance boost when iterating. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pools iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Types of storage iterated by the view. * @tparam Exclude Types of storage used to filter the view. */ template class basic_view, exclude_t> { using underlying_type = std::common_type_t; using basic_common_type = std::common_type_t; template friend class basic_view; template static constexpr std::size_t index_of = type_list_index_v, type_list>; [[nodiscard]] auto opaque_check_set() const noexcept { std::array other{}; std::apply([&other, pos = 0u, view = view](const auto *...curr) mutable { ((curr == view ? void() : void(other[pos++] = curr)), ...); }, pools); return other; } [[nodiscard]] auto filter_as_array() const noexcept { return std::apply([](const auto *...curr) { return std::array{curr...}; }, filter); } template [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { if constexpr(Curr == Other) { return std::forward_as_tuple(std::get(curr)...); } else { return storage().get_as_tuple(std::get<0>(curr)); } } [[nodiscard]] auto reject(const underlying_type entt) const noexcept { return std::apply([entt](const auto *...curr) { return (curr->contains(entt) || ...); }, filter); } template void each(Func &func, std::index_sequence) const { for(const auto curr: storage().each()) { if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && ((Curr == Index || storage().contains(entt)) && ...) && !reject(entt)) { if constexpr(is_applicable_v{}, std::declval().get({})))>) { std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); } else { std::apply(func, std::tuple_cat(dispatch_get(curr)...)); } } } } template void pick_and_each(Func &func, std::index_sequence seq) const { ((&storage() == view ? each(func, seq) : void()), ...); } public: /*! @brief Underlying entity identifier. */ using entity_type = underlying_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = basic_common_type; /*! @brief Bidirectional iterator type. */ using iterator = internal::view_iterator; /*! @brief Iterable view type. */ using iterable = iterable_adaptor>; /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{}, view{} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param exclude The storage for the types used to filter the view. */ basic_view(Get &...value, Exclude &...exclude) noexcept : pools{&value...}, filter{&exclude...}, view{[](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }(&value...)} {} /** * @brief Constructs a multi-type view from a set of storage classes. * @param value The storage for the types to iterate. * @param excl The storage for the types used to filter the view. */ basic_view(std::tuple value, std::tuple excl = {}) noexcept : pools{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, value)}, filter{std::apply([](auto &...curr) { return std::make_tuple(&curr...); }, excl)}, view{std::apply([](const base_type *first, const auto *...other) { ((first = other->size() < first->size() ? other : first), ...); return first; }, pools)} {} /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Type Type of component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template [[nodiscard]] basic_view use() const noexcept { return use>(); } /** * @brief Creates a new view driven by a given component in its iterations. * @tparam Index Index of the component used to drive the iteration. * @return A new view driven by the given component in its iterations. */ template [[nodiscard]] basic_view use() const noexcept { basic_view other{*this}; other.view = &storage(); return other; } /** * @brief Updates the internal leading view if required. * @return A newly created and internally optimized view. */ [[nodiscard]] basic_view refresh() const noexcept { return std::apply([](auto *...elem) { return basic_view{*elem...}; }, std::tuple_cat(pools, filter)); } /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return *view; } /** * @brief Returns the storage for a given component type. * @tparam Comp Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return storage>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Estimates the number of entities iterated by the view. * @return Estimated number of entities iterated by the view. */ [[nodiscard]] size_type size_hint() const noexcept { return view->size(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { return iterator{view->begin(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return iterator{view->end(), view->end(), opaque_check_set(), filter_as_array()}; } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { const auto it = begin(); return it != end() ? *it : null; } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { auto it = view->rbegin(); for(const auto last = view->rend(); it != last && !contains(*it); ++it) {} return it == view->rend() ? null : *it; } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? iterator{view->find(entt), view->end(), opaque_check_set(), filter_as_array()} : end(); } /** * @brief Returns the components assigned to the given entity. * @param entt A valid identifier. * @return The components assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return view != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return std::apply([entt](const auto *...curr) { return (curr->contains(entt) && ...); }, pools) && std::apply([entt](const auto *...curr) { return (!curr->contains(entt) && ...); }, filter); } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Types of components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); } else if constexpr(sizeof...(Type) == 1) { return (storage>().get(entt), ...); } else { return std::tuple_cat(storage>().get_as_tuple(entt)...); } } /** * @brief Returns the components assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam First Index of a component to get. * @tparam Other Indexes of other components to get. * @param entt A valid identifier. * @return The components assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Other) == 0) { return storage().get(entt); } else { return std::tuple_cat(storage().get_as_tuple(entt), storage().get_as_tuple(entt)...); } } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a set of references to non-empty components. The * _constness_ of the components is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &...); * void(Type &...); * @endcode * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { pick_and_each(func, std::index_sequence_for{}); } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a set of references to its non-empty components. The _constness_ of the * components is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { return {internal::extended_view_iterator{begin(), pools}, internal::extended_view_iterator{end(), pools}}; } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam OGet Component list of the view to combine with. * @tparam OExclude Filter list of the view to combine with. * @param other The view to combine with. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { return std::make_from_tuple, exclude_t>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, filter, other.filter))); } private: std::tuple pools; std::tuple filter; const base_type *view; }; /** * @brief Single component view specialization. * * Single component views are specialized in order to get a boost in terms of * performance. This kind of views can access the underlying data structure * directly and avoid superfluous checks. * * @b Important * * Iterators aren't invalidated if: * * * New elements are added to the storage. * * The entity currently pointed is modified (for example, components are added * or removed from it). * * The entity currently pointed is destroyed. * * In all other cases, modifying the pool iterated by the view in any way * invalidates all the iterators and using them results in undefined behavior. * * @tparam Get Type of storage iterated by the view. */ template class basic_view, exclude_t<>, std::void_t::in_place_delete>>> { template friend class basic_view; public: /*! @brief Underlying entity identifier. */ using entity_type = typename Get::entity_type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Common type among all storage types. */ using base_type = typename Get::base_type; /*! @brief Random access iterator type. */ using iterator = typename base_type::iterator; /*! @brief Reversed iterator type. */ using reverse_iterator = typename base_type::reverse_iterator; /*! @brief Iterable view type. */ using iterable = decltype(std::declval().each()); /*! @brief Default constructor to use to create empty, invalid views. */ basic_view() noexcept : pools{}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(Get &ref) noexcept : pools{&ref}, filter{} {} /** * @brief Constructs a single-type view from a storage class. * @param ref The storage for the type to iterate. */ basic_view(std::tuple ref, std::tuple<> = {}) noexcept : pools{&std::get<0>(ref)}, filter{} {} /** * @brief Returns the leading storage of a view. * @return The leading storage of the view. */ [[nodiscard]] const base_type &handle() const noexcept { return storage(); } /** * @brief Returns the storage for a given component type. * @tparam Type Type of component of which to return the storage. * @return The storage for the given component type. */ template [[nodiscard]] decltype(auto) storage() const noexcept { static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); return storage<0>(); } /** * @brief Returns the storage for a given index. * @tparam Index Index of the storage to return. * @return The storage for the given index. */ template [[nodiscard]] decltype(auto) storage() const noexcept { return *std::get(pools); } /** * @brief Returns the number of entities that have the given component. * @return Number of entities that have the given component. */ [[nodiscard]] size_type size() const noexcept { return handle().size(); } /** * @brief Checks whether a view is empty. * @return True if the view is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handle().empty(); } /** * @brief Returns an iterator to the first entity of the view. * * The returned iterator points to the first entity of the view. If the view * is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first entity of the view. */ [[nodiscard]] iterator begin() const noexcept { return handle().begin(); } /** * @brief Returns an iterator that is past the last entity of the view. * * The returned iterator points to the entity following the last entity of * the view. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the entity following the last entity of the view. */ [[nodiscard]] iterator end() const noexcept { return handle().end(); } /** * @brief Returns an iterator to the first entity of the reversed view. * * The returned iterator points to the first entity of the reversed view. If * the view is empty, the returned iterator will be equal to `rend()`. * * @return An iterator to the first entity of the reversed view. */ [[nodiscard]] reverse_iterator rbegin() const noexcept { return handle().rbegin(); } /** * @brief Returns an iterator that is past the last entity of the reversed * view. * * The returned iterator points to the entity following the last entity of * the reversed view. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the entity following the last entity of the * reversed view. */ [[nodiscard]] reverse_iterator rend() const noexcept { return handle().rend(); } /** * @brief Returns the first entity of the view, if any. * @return The first entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type front() const noexcept { return empty() ? null : *begin(); } /** * @brief Returns the last entity of the view, if any. * @return The last entity of the view if one exists, the null entity * otherwise. */ [[nodiscard]] entity_type back() const noexcept { return empty() ? null : *rbegin(); } /** * @brief Finds an entity. * @param entt A valid identifier. * @return An iterator to the given entity if it's found, past the end * iterator otherwise. */ [[nodiscard]] iterator find(const entity_type entt) const noexcept { return contains(entt) ? handle().find(entt) : end(); } /** * @brief Returns the identifier that occupies the given position. * @param pos Position of the element to return. * @return The identifier that occupies the given position. */ [[nodiscard]] entity_type operator[](const size_type pos) const { return begin()[pos]; } /** * @brief Returns the component assigned to the given entity. * @param entt A valid identifier. * @return The component assigned to the given entity. */ [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { return storage().get(entt); } /** * @brief Checks if a view is properly initialized. * @return True if the view is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return std::get<0>(pools) != nullptr; } /** * @brief Checks if a view contains an entity. * @param entt A valid identifier. * @return True if the view contains the given entity, false otherwise. */ [[nodiscard]] bool contains(const entity_type entt) const noexcept { return handle().contains(entt); } /** * @brief Returns the component assigned to the given entity. * * @warning * Attempting to use an entity that doesn't belong to the view results in * undefined behavior. * * @tparam Type Type or index of the component to get. * @param entt A valid identifier. * @return The component assigned to the entity. */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { if constexpr(sizeof...(Type) == 0) { return storage().get_as_tuple(entt); } else { static_assert((std::is_same_v, typename Get::value_type> && ...), "Invalid component type"); return storage().get(entt); } } /*! @copydoc get */ template [[nodiscard]] decltype(auto) get(const entity_type entt) const { return storage().get(entt); } /** * @brief Iterates entities and components and applies the given function * object to them. * * The function object is invoked for each entity. It is provided with the * entity itself and a reference to the component if it's a non-empty one. * The _constness_ of the component is as requested.
* The signature of the function must be equivalent to one of the following * forms: * * @code{.cpp} * void(const entity_type, Type &); * void(typename Type &); * @endcode * * @note * Empty types aren't explicitly instantiated and therefore they are never * returned during iterations. * * @tparam Func Type of the function object to invoke. * @param func A valid function object. */ template void each(Func func) const { if constexpr(is_applicable_v) { for(const auto pack: each()) { std::apply(func, pack); } } else if constexpr(ignore_as_empty_v) { for(size_type pos{}, last = size(); pos < last; ++pos) { func(); } } else { for(auto &&component: storage()) { func(component); } } } /** * @brief Returns an iterable object to use to _visit_ a view. * * The iterable object returns a tuple that contains the current entity and * a reference to its component if it's a non-empty one. The _constness_ of * the component is as requested. * * @return An iterable object to use to _visit_ the view. */ [[nodiscard]] iterable each() const noexcept { return storage().each(); } /** * @brief Combines two views in a _more specific_ one (friend function). * @tparam OGet Component list of the view to combine with. * @tparam OExclude Filter list of the view to combine with. * @param other The view to combine with. * @return A more specific view. */ template [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { return std::make_from_tuple, exclude_t>>( std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, std::tuple_cat(pools, other.pools, other.filter))); } private: std::tuple pools; std::tuple<> filter; }; /** * @brief Deduction guide. * @tparam Type Type of storage classes used to create the view. * @param storage The storage for the types to iterate. */ template basic_view(Type &...storage) -> basic_view, exclude_t<>>; /** * @brief Deduction guide. * @tparam Get Types of components iterated by the view. * @tparam Exclude Types of components used to filter the view. */ template basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; } // namespace entt #endif // #include "graph/adjacency_matrix.hpp" #ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP #define ENTT_GRAPH_ADJACENCY_MATRIX_HPP #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_GRAPH_FWD_HPP #define ENTT_GRAPH_FWD_HPP #include #include // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /*! @brief Undirected graph category tag. */ struct directed_tag {}; /*! @brief Directed graph category tag. */ struct undirected_tag: directed_tag {}; template> class adjacency_matrix; template> class basic_flow; /*! @brief Alias declaration for the most common use case. */ using flow = basic_flow<>; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class edge_iterator { using size_type = std::size_t; public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr edge_iterator() noexcept : it{}, vert{}, pos{}, last{}, offset{} {} constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept : it{std::move(base)}, vert{vertices}, pos{from}, last{to}, offset{step} { for(; pos != last && !it[pos]; pos += offset) {} } constexpr edge_iterator &operator++() noexcept { for(pos += offset; pos != last && !it[pos]; pos += offset) {} return *this; } constexpr edge_iterator operator++(int) noexcept { edge_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::make_pair(pos / vert, pos % vert); } template friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; private: It it; size_type vert; size_type pos; size_type last; size_type offset{}; }; template [[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return lhs.pos == rhs.pos; } template [[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic implementation of a directed adjacency matrix. * @tparam Category Either a directed or undirected category tag. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class adjacency_matrix { using alloc_traits = std::allocator_traits; static_assert(std::is_base_of_v, "Invalid graph category"); static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Vertex type. */ using vertex_type = size_type; /*! @brief Edge type. */ using edge_type = std::pair; /*! @brief Vertex iterator type. */ using vertex_iterator = iota_iterator; /*! @brief Edge iterator type. */ using edge_iterator = internal::edge_iterator; /*! @brief Out edge iterator type. */ using out_edge_iterator = edge_iterator; /*! @brief In edge iterator type. */ using in_edge_iterator = edge_iterator; /*! @brief Graph category tag. */ using graph_category = Category; /*! @brief Default constructor. */ adjacency_matrix() noexcept(noexcept(allocator_type{})) : adjacency_matrix{0u} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit adjacency_matrix(const allocator_type &allocator) noexcept : adjacency_matrix{0u, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied number of vertices. * @param vertices Number of vertices. * @param allocator The allocator to use. */ adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) : matrix{vertices * vertices, allocator}, vert{vertices} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ adjacency_matrix(const adjacency_matrix &other) : adjacency_matrix{other, other.get_allocator()} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) : matrix{other.matrix, allocator}, vert{other.vert} {} /** * @brief Move constructor. * @param other The instance to move from. */ adjacency_matrix(adjacency_matrix &&other) noexcept : adjacency_matrix{std::move(other), other.get_allocator()} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) : matrix{std::move(other.matrix), allocator}, vert{std::exchange(other.vert, 0u)} {} /** * @brief Default copy assignment operator. * @param other The instance to copy from. * @return This container. */ adjacency_matrix &operator=(const adjacency_matrix &other) { matrix = other.matrix; vert = other.vert; return *this; } /** * @brief Default move assignment operator. * @param other The instance to move from. * @return This container. */ adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { matrix = std::move(other.matrix); vert = std::exchange(other.vert, 0u); return *this; } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return matrix.get_allocator(); } /*! @brief Clears the adjacency matrix. */ void clear() noexcept { matrix.clear(); vert = {}; } /** * @brief Exchanges the contents with those of a given adjacency matrix. * @param other Adjacency matrix to exchange the content with. */ void swap(adjacency_matrix &other) { using std::swap; swap(matrix, other.matrix); swap(vert, other.vert); } /** * @brief Returns the number of vertices. * @return The number of vertices. */ [[nodiscard]] size_type size() const noexcept { return vert; } /** * @brief Returns an iterable object to visit all vertices of a matrix. * @return An iterable object to visit all vertices of a matrix. */ [[nodiscard]] iterable_adaptor vertices() const noexcept { return {0u, vert}; } /** * @brief Returns an iterable object to visit all edges of a matrix. * @return An iterable object to visit all edges of a matrix. */ [[nodiscard]] iterable_adaptor edges() const noexcept { const auto it = matrix.cbegin(); const auto sz = matrix.size(); return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; } /** * @brief Returns an iterable object to visit all out edges of a vertex. * @param vertex The vertex of which to return all out edges. * @return An iterable object to visit all out edges of a vertex. */ [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; const auto to = vertex * vert + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } /** * @brief Returns an iterable object to visit all in edges of a vertex. * @param vertex The vertex of which to return all in edges. * @return An iterable object to visit all in edges of a vertex. */ [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; const auto to = vert * (vert - 1u) + vertex; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } /** * @brief Resizes an adjacency matrix. * @param vertices The new number of vertices. */ void resize(const size_type vertices) { adjacency_matrix other{vertices, get_allocator()}; for(auto [lhs, rhs]: edges()) { other.insert(lhs, rhs); } other.swap(*this); } /** * @brief Inserts an edge into the adjacency matrix, if it does not exist. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 1u; } const auto inserted = !std::exchange(matrix[pos], 1u); return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; } /** * @brief Removes the edge associated with a pair of given vertices. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return Number of elements removed (either 0 or 1). */ size_type erase(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 0u; } return std::exchange(matrix[pos], 0u); } /** * @brief Checks if an adjacency matrix contains a given edge. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return True if there is such an edge, false otherwise. */ [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { const auto pos = lhs * vert + rhs; return pos < matrix.size() && matrix[pos]; } private: container_type matrix; size_type vert; }; } // namespace entt #endif // #include "graph/dot.hpp" #ifndef ENTT_GRAPH_DOT_HPP #define ENTT_GRAPH_DOT_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @brief Outputs a graph in dot format. * @tparam Graph Graph type, valid as long as it exposes edges and vertices. * @tparam Writer Vertex decorator type. * @param out A standard output stream. * @param graph The graph to output. * @param writer Vertex decorator object. */ template void dot(std::ostream &out, const Graph &graph, Writer writer) { static_assert(std::is_base_of_v, "Invalid graph category"); if constexpr(std::is_same_v) { out << "graph{"; } else { out << "digraph{"; } for(auto &&vertex: graph.vertices()) { out << vertex << "["; writer(out, vertex); out << "];"; } for(auto [lhs, rhs]: graph.edges()) { if constexpr(std::is_same_v) { out << lhs << "--" << rhs << ";"; } else { out << lhs << "->" << rhs << ";"; } } out << "}"; } /** * @brief Outputs a graph in dot format. * @tparam Graph Graph type, valid as long as it exposes edges and vertices. * @param out A standard output stream. * @param graph The graph to output. */ template void dot(std::ostream &out, const Graph &graph) { return dot(out, graph, [](auto &&...) {}); } } // namespace entt #endif // #include "graph/flow.hpp" #ifndef ENTT_GRAPH_FLOW_HPP #define ENTT_GRAPH_FLOW_HPP #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../container/dense_set.hpp" #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" // #include "../core/memory.hpp" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class dense_set_iterator final { template friend class dense_set_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; constexpr dense_set_iterator() noexcept : it{} {} constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_set_local_iterator final { template friend class dense_set_local_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for unique objects of a given type. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on its hash. Elements with the same hash code * appear in the same bucket. * * @tparam Type Value type of the associative container. * @tparam Hash Type of function to use to hash the values. * @tparam KeyEqual Type of function to use to compare the values for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_set { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = std::pair; using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&value) { const auto index = value_to_bucket(value); if(auto it = constrained_find(value, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Type; /*! @brief Value type of the container. */ using value_type = Type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the elements. */ using hasher = Hash; /*! @brief Type of function to use to compare the elements for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Random access iterator type. */ using iterator = internal::dense_set_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::dense_set_iterator; /*! @brief Forward iterator type. */ using local_iterator = internal::dense_set_local_iterator; /*! @brief Constant forward iterator type. */ using const_local_iterator = internal::dense_set_local_iterator; /*! @brief Default constructor. */ dense_set() : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_set(const allocator_type &allocator) : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const allocator_type &allocator) : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_set(const dense_set &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_set(const dense_set &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_set(dense_set &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_set &operator=(const dense_set &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if it does not exist. * @param value An element to insert into the container. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value)); } /** * @brief Inserts elements into the container, if they do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Constructs an element in-place, if it does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace(Args &&...args) { if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); const auto index = value_to_bucket(node.second); if(auto it = constrained_find(node.second, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.first, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(*pos); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].second); } return (begin() + dist); } /** * @brief Removes the element associated with a given value. * @param value Value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const value_type &value) { for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { if(packed.second()(packed.first()[*curr].second, value)) { const auto index = *curr; *curr = packed.first()[*curr].first; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Returns the number of elements matching a value (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const value_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given value. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const value_type &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const value_type &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Finds an element that compares _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Returns a range containing all elements with a given value. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const value_type &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const value_type &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const value_type &value) const { return (find(value) != cend()); } /** * @brief Checks if the container contains an element that compares * _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &value) const { return (find(value) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given element. * @param value The value of the element to examine. * @return The bucket for the given element. */ [[nodiscard]] size_type bucket(const value_type &value) const { return value_to_bucket(value); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); packed.first()[pos].first = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the elements. * @return The function used to hash the elements. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare elements for equality. * @return The function used to compare elements for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "adjacency_matrix.hpp" #ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP #define ENTT_GRAPH_ADJACENCY_MATRIX_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/iterator.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class edge_iterator { using size_type = std::size_t; public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr edge_iterator() noexcept : it{}, vert{}, pos{}, last{}, offset{} {} constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept : it{std::move(base)}, vert{vertices}, pos{from}, last{to}, offset{step} { for(; pos != last && !it[pos]; pos += offset) {} } constexpr edge_iterator &operator++() noexcept { for(pos += offset; pos != last && !it[pos]; pos += offset) {} return *this; } constexpr edge_iterator operator++(int) noexcept { edge_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::make_pair(pos / vert, pos % vert); } template friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; private: It it; size_type vert; size_type pos; size_type last; size_type offset{}; }; template [[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return lhs.pos == rhs.pos; } template [[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic implementation of a directed adjacency matrix. * @tparam Category Either a directed or undirected category tag. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class adjacency_matrix { using alloc_traits = std::allocator_traits; static_assert(std::is_base_of_v, "Invalid graph category"); static_assert(std::is_same_v, "Invalid value type"); using container_type = std::vector>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Vertex type. */ using vertex_type = size_type; /*! @brief Edge type. */ using edge_type = std::pair; /*! @brief Vertex iterator type. */ using vertex_iterator = iota_iterator; /*! @brief Edge iterator type. */ using edge_iterator = internal::edge_iterator; /*! @brief Out edge iterator type. */ using out_edge_iterator = edge_iterator; /*! @brief In edge iterator type. */ using in_edge_iterator = edge_iterator; /*! @brief Graph category tag. */ using graph_category = Category; /*! @brief Default constructor. */ adjacency_matrix() noexcept(noexcept(allocator_type{})) : adjacency_matrix{0u} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit adjacency_matrix(const allocator_type &allocator) noexcept : adjacency_matrix{0u, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied number of vertices. * @param vertices Number of vertices. * @param allocator The allocator to use. */ adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) : matrix{vertices * vertices, allocator}, vert{vertices} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ adjacency_matrix(const adjacency_matrix &other) : adjacency_matrix{other, other.get_allocator()} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) : matrix{other.matrix, allocator}, vert{other.vert} {} /** * @brief Move constructor. * @param other The instance to move from. */ adjacency_matrix(adjacency_matrix &&other) noexcept : adjacency_matrix{std::move(other), other.get_allocator()} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) : matrix{std::move(other.matrix), allocator}, vert{std::exchange(other.vert, 0u)} {} /** * @brief Default copy assignment operator. * @param other The instance to copy from. * @return This container. */ adjacency_matrix &operator=(const adjacency_matrix &other) { matrix = other.matrix; vert = other.vert; return *this; } /** * @brief Default move assignment operator. * @param other The instance to move from. * @return This container. */ adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { matrix = std::move(other.matrix); vert = std::exchange(other.vert, 0u); return *this; } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return matrix.get_allocator(); } /*! @brief Clears the adjacency matrix. */ void clear() noexcept { matrix.clear(); vert = {}; } /** * @brief Exchanges the contents with those of a given adjacency matrix. * @param other Adjacency matrix to exchange the content with. */ void swap(adjacency_matrix &other) { using std::swap; swap(matrix, other.matrix); swap(vert, other.vert); } /** * @brief Returns the number of vertices. * @return The number of vertices. */ [[nodiscard]] size_type size() const noexcept { return vert; } /** * @brief Returns an iterable object to visit all vertices of a matrix. * @return An iterable object to visit all vertices of a matrix. */ [[nodiscard]] iterable_adaptor vertices() const noexcept { return {0u, vert}; } /** * @brief Returns an iterable object to visit all edges of a matrix. * @return An iterable object to visit all edges of a matrix. */ [[nodiscard]] iterable_adaptor edges() const noexcept { const auto it = matrix.cbegin(); const auto sz = matrix.size(); return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; } /** * @brief Returns an iterable object to visit all out edges of a vertex. * @param vertex The vertex of which to return all out edges. * @return An iterable object to visit all out edges of a vertex. */ [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex * vert; const auto to = vertex * vert + vert; return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; } /** * @brief Returns an iterable object to visit all in edges of a vertex. * @param vertex The vertex of which to return all in edges. * @return An iterable object to visit all in edges of a vertex. */ [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { const auto it = matrix.cbegin(); const auto from = vertex; const auto to = vert * (vert - 1u) + vertex; return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; } /** * @brief Resizes an adjacency matrix. * @param vertices The new number of vertices. */ void resize(const size_type vertices) { adjacency_matrix other{vertices, get_allocator()}; for(auto [lhs, rhs]: edges()) { other.insert(lhs, rhs); } other.swap(*this); } /** * @brief Inserts an edge into the adjacency matrix, if it does not exist. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 1u; } const auto inserted = !std::exchange(matrix[pos], 1u); return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; } /** * @brief Removes the edge associated with a pair of given vertices. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return Number of elements removed (either 0 or 1). */ size_type erase(const vertex_type lhs, const vertex_type rhs) { const auto pos = lhs * vert + rhs; if constexpr(std::is_same_v) { const auto rev = rhs * vert + lhs; ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); matrix[rev] = 0u; } return std::exchange(matrix[pos], 0u); } /** * @brief Checks if an adjacency matrix contains a given edge. * @param lhs The left hand vertex of the edge. * @param rhs The right hand vertex of the edge. * @return True if there is such an edge, false otherwise. */ [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { const auto pos = lhs * vert + rhs; return pos < matrix.size() && matrix[pos]; } private: container_type matrix; size_type vert; }; } // namespace entt #endif // #include "fwd.hpp" namespace entt { /** * @brief Utility class for creating task graphs. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_flow { using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; void emplace(const id_type res, const bool is_rw) { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); if(!deps.contains(res) && sync_on != vertices.size()) { deps[res].emplace_back(sync_on, true); } deps[res].emplace_back(index.first(), is_rw); } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Iterable task list. */ using iterable = iterable_adaptor; /*! @brief Default constructor. */ basic_flow() : basic_flow{allocator_type{}} {} /** * @brief Constructs a flow builder with a given allocator. * @param allocator The allocator to use. */ explicit basic_flow(const allocator_type &allocator) : index{0u, allocator}, vertices{}, deps{}, sync_on{} {} /*! @brief Default copy constructor. */ basic_flow(const basic_flow &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ basic_flow(const basic_flow &other, const allocator_type &allocator) : index{other.index.first(), allocator}, vertices{other.vertices, allocator}, deps{other.deps, allocator}, sync_on{other.sync_on} {} /*! @brief Default move constructor. */ basic_flow(basic_flow &&) noexcept = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_flow(basic_flow &&other, const allocator_type &allocator) : index{other.index.first(), allocator}, vertices{std::move(other.vertices), allocator}, deps{std::move(other.deps), allocator}, sync_on{other.sync_on} {} /** * @brief Default copy assignment operator. * @return This flow builder. */ basic_flow &operator=(const basic_flow &) = default; /** * @brief Default move assignment operator. * @return This flow builder. */ basic_flow &operator=(basic_flow &&) noexcept = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return allocator_type{index.second()}; } /** * @brief Returns the identifier at specified location. * @param pos Position of the identifier to return. * @return The requested identifier. */ [[nodiscard]] id_type operator[](const size_type pos) const { return vertices.cbegin()[pos]; } /*! @brief Clears the flow builder. */ void clear() noexcept { index.first() = sync_on = {}; vertices.clear(); deps.clear(); } /** * @brief Exchanges the contents with those of a given flow builder. * @param other Flow builder to exchange the content with. */ void swap(basic_flow &other) { using std::swap; std::swap(index, other.index); std::swap(vertices, other.vertices); std::swap(deps, other.deps); std::swap(sync_on, other.sync_on); } /** * @brief Returns the number of tasks. * @return The number of tasks. */ [[nodiscard]] size_type size() const noexcept { return vertices.size(); } /** * @brief Binds a task to a flow builder. * @param value Task identifier. * @return This flow builder. */ basic_flow &bind(const id_type value) { sync_on += (sync_on == vertices.size()); const auto it = vertices.emplace(value).first; index.first() = size_type(it - vertices.begin()); return *this; } /** * @brief Turns the current task into a sync point. * @return This flow builder. */ basic_flow &sync() { ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); sync_on = index.first(); for(const auto &elem: deps) { elem.second.emplace_back(sync_on, true); } return *this; } /** * @brief Assigns a resource to the current task with a given access mode. * @param res Resource identifier. * @param is_rw Access mode. * @return This flow builder. */ basic_flow &set(const id_type res, bool is_rw = false) { emplace(res, is_rw); return *this; } /** * @brief Assigns a read-only resource to the current task. * @param res Resource identifier. * @return This flow builder. */ basic_flow &ro(const id_type res) { emplace(res, false); return *this; } /** * @brief Assigns a range of read-only resources to the current task. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return This flow builder. */ template std::enable_if_t::value_type>, id_type>, basic_flow &> ro(It first, It last) { for(; first != last; ++first) { emplace(*first, false); } return *this; } /** * @brief Assigns a writable resource to the current task. * @param res Resource identifier. * @return This flow builder. */ basic_flow &rw(const id_type res) { emplace(res, true); return *this; } /** * @brief Assigns a range of writable resources to the current task. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return This flow builder. */ template std::enable_if_t::value_type>, id_type>, basic_flow &> rw(It first, It last) { for(; first != last; ++first) { emplace(*first, true); } return *this; } /** * @brief Generates a task graph for the current content. * @return The adjacency matrix of the task graph. */ [[nodiscard]] adjacency_matrix graph() const { const auto length = vertices.size(); adjacency_matrix matrix{length}; // creates the adjacency matrix for(const auto &elem: deps) { const auto last = elem.second.cend(); auto it = elem.second.cbegin(); while(it != last) { if(it->second) { // rw item if(auto curr = it++; it != last) { if(it->second) { matrix.insert(curr->first, it->first); } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { for(; it != next; ++it) { matrix.insert(curr->first, it->first); matrix.insert(it->first, next->first); } } else { for(; it != next; ++it) { matrix.insert(curr->first, it->first); } } } } else { // ro item (first iteration only) if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { for(; it != next; ++it) { matrix.insert(it->first, next->first); } } else { it = last; } } } } // computes the transitive closure for(std::size_t vk{}; vk < length; ++vk) { for(std::size_t vi{}; vi < length; ++vi) { for(std::size_t vj{}; vj < length; ++vj) { if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { matrix.insert(vi, vj); } } } } // applies the transitive reduction for(std::size_t vert{}; vert < length; ++vert) { matrix.erase(vert, vert); } for(std::size_t vj{}; vj < length; ++vj) { for(std::size_t vi{}; vi < length; ++vi) { if(matrix.contains(vi, vj)) { for(std::size_t vk{}; vk < length; ++vk) { if(matrix.contains(vj, vk)) { matrix.erase(vi, vk); } } } } } return matrix; } private: compressed_pair index; task_container_type vertices; deps_container_type deps; size_type sync_on; }; } // namespace entt #endif // #include "locator/locator.hpp" #ifndef ENTT_LOCATOR_LOCATOR_HPP #define ENTT_LOCATOR_LOCATOR_HPP #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { /** * @brief Service locator, nothing more. * * A service locator is used to do what it promises: locate services.
* Usually service locators are tightly bound to the services they expose and * thus it's hard to define a general purpose class to do that. This tiny class * tries to fill the gap and to get rid of the burden of defining a different * specific locator for each application. * * @note * Users shouldn't retain references to a service. The recommended way is to * retrieve the service implementation currently set each and every time the * need for it arises. The risk is to incur in unexpected behaviors otherwise. * * @tparam Service Service type. */ template class locator final { class service_handle { friend class locator; std::shared_ptr value{}; }; public: /*! @brief Service type. */ using type = Service; /*! @brief Service node type. */ using node_type = service_handle; /*! @brief Default constructor, deleted on purpose. */ locator() = delete; /*! @brief Default destructor, deleted on purpose. */ ~locator() = delete; /** * @brief Checks whether a service locator contains a value. * @return True if the service locator contains a value, false otherwise. */ [[nodiscard]] static bool has_value() noexcept { return (service != nullptr); } /** * @brief Returns a reference to a valid service, if any. * * @warning * Invoking this function can result in undefined behavior if the service * hasn't been set yet. * * @return A reference to the service currently set, if any. */ [[nodiscard]] static Service &value() noexcept { ENTT_ASSERT(has_value(), "Service not available"); return *service; } /** * @brief Returns a service if available or sets it from a fallback type. * * Arguments are used only if a service doesn't already exist. In all other * cases, they are discarded. * * @tparam Args Types of arguments to use to construct the fallback service. * @tparam Impl Fallback service type. * @param args Parameters to use to construct the fallback service. * @return A reference to a valid service. */ template [[nodiscard]] static Service &value_or(Args &&...args) { return service ? *service : emplace(std::forward(args)...); } /** * @brief Sets or replaces a service. * @tparam Impl Service type. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ template static Service &emplace(Args &&...args) { service = std::make_shared(std::forward(args)...); return *service; } /** * @brief Sets or replaces a service using a given allocator. * @tparam Impl Service type. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the service. * @param alloc The allocator to use. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ template static Service &allocate_emplace(Allocator alloc, Args &&...args) { service = std::allocate_shared(alloc, std::forward(args)...); return *service; } /** * @brief Returns a handle to the underlying service. * @return A handle to the underlying service. */ static node_type handle() noexcept { node_type node{}; node.value = service; return node; } /** * @brief Resets or replaces a service. * @param other Optional handle with which to replace the service. */ static void reset(const node_type &other = {}) noexcept { service = other.value; } private: // std::shared_ptr because of its type erased allocator which is useful here inline static std::shared_ptr service{}; }; } // namespace entt #endif // #include "meta/adl_pointer.hpp" #ifndef ENTT_META_ADL_POINTER_HPP #define ENTT_META_ADL_POINTER_HPP namespace entt { /** * @brief ADL based lookup function for dereferencing meta pointer-like types. * @tparam Type Element type. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ template decltype(auto) dereference_meta_pointer_like(const Type &value) { return *value; } /** * @brief Fake ADL based lookup function for meta pointer-like types. * @tparam Type Element type. */ template struct adl_meta_pointer_like { /** * @brief Uses the default ADL based lookup method to resolve the call. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ static decltype(auto) dereference(const Type &value) { return dereference_meta_pointer_like(value); } }; } // namespace entt #endif // #include "meta/container.hpp" #ifndef ENTT_META_CONTAINER_HPP #define ENTT_META_CONTAINER_HPP #include #include #include #include #include #include #include #include #include #include // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../container/dense_set.hpp" #ifndef ENTT_CONTAINER_DENSE_SET_HPP #define ENTT_CONTAINER_DENSE_SET_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/compressed_pair.hpp" // #include "../core/memory.hpp" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class dense_set_iterator final { template friend class dense_set_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::random_access_iterator_tag; constexpr dense_set_iterator() noexcept : it{} {} constexpr dense_set_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_set_iterator(const dense_set_iterator &other) noexcept : it{other.it} {} constexpr dense_set_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_set_iterator operator++(int) noexcept { dense_set_iterator orig = *this; return ++(*this), orig; } constexpr dense_set_iterator &operator--() noexcept { return --it, *this; } constexpr dense_set_iterator operator--(int) noexcept { dense_set_iterator orig = *this; return operator--(), orig; } constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_set_iterator operator+(const difference_type value) const noexcept { dense_set_iterator copy = *this; return (copy += value); } constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_set_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return it[value].second; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it->second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } template friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; template friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_set_local_iterator final { template friend class dense_set_local_iterator; public: using value_type = typename It::value_type::second_type; using pointer = const value_type *; using reference = const value_type &; using difference_type = std::ptrdiff_t; using iterator_category = std::forward_iterator_tag; constexpr dense_set_local_iterator() noexcept : it{}, offset{} {} constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_set_local_iterator &operator++() noexcept { return offset = it[offset].first, *this; } constexpr dense_set_local_iterator operator++(int) noexcept { dense_set_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return std::addressof(it[offset].second); } [[nodiscard]] constexpr reference operator*() const noexcept { return *operator->(); } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for unique objects of a given type. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on its hash. Elements with the same hash code * appear in the same bucket. * * @tparam Type Value type of the associative container. * @tparam Hash Type of function to use to hash the values. * @tparam KeyEqual Type of function to use to compare the values for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_set { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = std::pair; using alloc_traits = std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { return fast_mod(static_cast(sparse.second()(value)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(*it, value)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&value) { const auto index = value_to_bucket(value); if(auto it = constrained_find(value, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].first) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Type; /*! @brief Value type of the container. */ using value_type = Type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the elements. */ using hasher = Hash; /*! @brief Type of function to use to compare the elements for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Random access iterator type. */ using iterator = internal::dense_set_iterator; /*! @brief Constant random access iterator type. */ using const_iterator = internal::dense_set_iterator; /*! @brief Forward iterator type. */ using local_iterator = internal::dense_set_local_iterator; /*! @brief Constant forward iterator type. */ using const_local_iterator = internal::dense_set_local_iterator; /*! @brief Default constructor. */ dense_set() : dense_set{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_set(const allocator_type &allocator) : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const allocator_type &allocator) : dense_set{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_set{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_set(const dense_set &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_set(const dense_set &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_set(dense_set &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_set &operator=(const dense_set &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if it does not exist. * @param value An element to insert into the container. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value)); } /** * @brief Inserts elements into the container, if they do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Constructs an element in-place, if it does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace(Args &&...args) { if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); const auto index = value_to_bucket(node.second); if(auto it = constrained_find(node.second, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.first, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(*pos); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].second); } return (begin() + dist); } /** * @brief Removes the element associated with a given value. * @param value Value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const value_type &value) { for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { if(packed.second()(packed.first()[*curr].second, value)) { const auto index = *curr; *curr = packed.first()[*curr].first; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_set &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Returns the number of elements matching a value (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const value_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given value. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const value_type &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const value_type &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Finds an element that compares _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return An iterator to an element with the given value. If no such * element is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) { return constrained_find(value, value_to_bucket(value)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &value) const { return constrained_find(value, value_to_bucket(value)); } /** * @brief Returns a range containing all elements with a given value. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const value_type &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const value_type &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) { const auto it = find(value); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &value) const { const auto it = find(value); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given value. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const value_type &value) const { return (find(value) != cend()); } /** * @brief Checks if the container contains an element that compares * _equivalent_ to a given value. * @tparam Other Type of an element to search for. * @param value Value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &value) const { return (find(value) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given element. * @param value The value of the element to examine. * @return The bucket for the given element. */ [[nodiscard]] size_type bucket(const value_type &value) const { return value_to_bucket(value); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = value_to_bucket(packed.first()[pos].second); packed.first()[pos].first = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the elements. * @return The function used to hash the elements. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare elements for equality. * @return The function used to compare elements for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt #endif // #include "context.hpp" #ifndef ENTT_META_CTX_HPP #define ENTT_META_CTX_HPP // #include "../container/dense_map.hpp" // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif namespace entt { class meta_ctx; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct meta_context { dense_map value{}; static inline meta_context &from(meta_ctx &ctx); static inline const meta_context &from(const meta_ctx &ctx); }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @brief Disambiguation tag for constructors and the like. */ class meta_ctx_arg_t final {}; /*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ inline constexpr meta_ctx_arg_t meta_ctx_arg{}; /*! @brief Opaque meta context type. */ class meta_ctx: private internal::meta_context { /*! @brief Attorney idiom like model to access the base class. */ friend struct internal::meta_context; }; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { return ctx; } inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { return ctx; } /** * Internal details not to be documented. * @endcond */ } // namespace entt #endif // #include "meta.hpp" #ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include // #include "../config/config.h" // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "fwd.hpp" // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class any_operation : std::uint8_t { copy, move, transfer, assign, destroy, compare, get }; enum class any_policy : std::uint8_t { owner, ref, cref }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_any { using operation = internal::any_operation; using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); struct storage_type { alignas(Align) std::byte data[Len + !Len]; }; template static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; if constexpr(in_situ) { element = value.owner() ? reinterpret_cast(&value.storage) : static_cast(value.instance); } else { element = static_cast(value.instance); } switch(op) { case operation::copy: if constexpr(std::is_copy_constructible_v) { static_cast(const_cast(other))->initialize(*element); } break; case operation::move: if constexpr(in_situ) { if(value.owner()) { return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; } } return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); case operation::transfer: if constexpr(std::is_move_assignable_v) { *const_cast(element) = std::move(*static_cast(const_cast(other))); return other; } [[fallthrough]]; case operation::assign: if constexpr(std::is_copy_assignable_v) { *const_cast(element) = *static_cast(other); return other; } break; case operation::destroy: if constexpr(in_situ) { element->~Type(); } else if constexpr(std::is_array_v) { delete[] element; } else { delete element; } break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } case operation::get: return element; } return nullptr; } template void initialize([[maybe_unused]] Args &&...args) { info = &type_id>>(); if constexpr(!std::is_void_v) { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); } } } } basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, mode{pol} {} public: /*! @brief Size of the internal storage. */ static constexpr auto length = Len; /*! @brief Alignment requirement. */ static constexpr auto alignment = Align; /*! @brief Default constructor. */ constexpr basic_any() noexcept : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_any(std::in_place_type_t, Args &&...args) : instance{}, info{}, vtable{}, mode{policy::owner} { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, basic_any>>> basic_any(Type &&value) : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any &other) : basic_any{} { if(other.vtable) { other.vtable(operation::copy, other, this); } } /** * @brief Move constructor. * @param other The instance to move from. */ basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, mode{other.mode} { if(other.vtable) { other.vtable(operation::move, other, this); } } /*! @brief Frees the internal storage, whatever it means. */ ~basic_any() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This any object. */ basic_any &operator=(const basic_any &other) { reset(); if(other.vtable) { other.vtable(operation::copy, other, this); } return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This any object. */ basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { other.vtable(operation::move, other, this); info = other.info; vtable = other.vtable; mode = other.mode; } return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This any object. */ template std::enable_if_t, basic_any>, basic_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] const type_info &type() const noexcept { return *info; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data() noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data(const type_info &req) noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&...args) { reset(); initialize(std::forward(args)...); } /** * @brief Assigns a value to the contained object without replacing it. * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } return false; } /*! @copydoc assign */ bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); } else { return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); } } return false; } /*! @brief Destroys contained object */ void reset() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; } /** * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } return (!vtable && !other.vtable); } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return True if the two objects differ in their content, false otherwise. */ [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { return !(*this == other); } /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } /** * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } private: union { const void *instance; storage_type storage; }; const type_info *info; vtable_type *vtable; policy mode; }; /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param data Target any object. * @return The element converted to the requested type. */ template Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); } else { return any_cast(data); } } else { auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } } /*! @copydoc any_cast */ template const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); } else { const auto &info = type_id>(); return static_cast(data->data(info)); } } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt #endif // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "fwd.hpp" // #include "hashed_string.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "../core/utility.hpp" // #include "../locator/locator.hpp" #ifndef ENTT_LOCATOR_LOCATOR_HPP #define ENTT_LOCATOR_LOCATOR_HPP #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif namespace entt { /** * @brief Service locator, nothing more. * * A service locator is used to do what it promises: locate services.
* Usually service locators are tightly bound to the services they expose and * thus it's hard to define a general purpose class to do that. This tiny class * tries to fill the gap and to get rid of the burden of defining a different * specific locator for each application. * * @note * Users shouldn't retain references to a service. The recommended way is to * retrieve the service implementation currently set each and every time the * need for it arises. The risk is to incur in unexpected behaviors otherwise. * * @tparam Service Service type. */ template class locator final { class service_handle { friend class locator; std::shared_ptr value{}; }; public: /*! @brief Service type. */ using type = Service; /*! @brief Service node type. */ using node_type = service_handle; /*! @brief Default constructor, deleted on purpose. */ locator() = delete; /*! @brief Default destructor, deleted on purpose. */ ~locator() = delete; /** * @brief Checks whether a service locator contains a value. * @return True if the service locator contains a value, false otherwise. */ [[nodiscard]] static bool has_value() noexcept { return (service != nullptr); } /** * @brief Returns a reference to a valid service, if any. * * @warning * Invoking this function can result in undefined behavior if the service * hasn't been set yet. * * @return A reference to the service currently set, if any. */ [[nodiscard]] static Service &value() noexcept { ENTT_ASSERT(has_value(), "Service not available"); return *service; } /** * @brief Returns a service if available or sets it from a fallback type. * * Arguments are used only if a service doesn't already exist. In all other * cases, they are discarded. * * @tparam Args Types of arguments to use to construct the fallback service. * @tparam Impl Fallback service type. * @param args Parameters to use to construct the fallback service. * @return A reference to a valid service. */ template [[nodiscard]] static Service &value_or(Args &&...args) { return service ? *service : emplace(std::forward(args)...); } /** * @brief Sets or replaces a service. * @tparam Impl Service type. * @tparam Args Types of arguments to use to construct the service. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ template static Service &emplace(Args &&...args) { service = std::make_shared(std::forward(args)...); return *service; } /** * @brief Sets or replaces a service using a given allocator. * @tparam Impl Service type. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the service. * @param alloc The allocator to use. * @param args Parameters to use to construct the service. * @return A reference to a valid service. */ template static Service &allocate_emplace(Allocator alloc, Args &&...args) { service = std::allocate_shared(alloc, std::forward(args)...); return *service; } /** * @brief Returns a handle to the underlying service. * @return A handle to the underlying service. */ static node_type handle() noexcept { node_type node{}; node.value = service; return node; } /** * @brief Resets or replaces a service. * @param other Optional handle with which to replace the service. */ static void reset(const node_type &other = {}) noexcept { service = other.value; } private: // std::shared_ptr because of its type erased allocator which is useful here inline static std::shared_ptr service{}; }; } // namespace entt #endif // #include "adl_pointer.hpp" #ifndef ENTT_META_ADL_POINTER_HPP #define ENTT_META_ADL_POINTER_HPP namespace entt { /** * @brief ADL based lookup function for dereferencing meta pointer-like types. * @tparam Type Element type. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ template decltype(auto) dereference_meta_pointer_like(const Type &value) { return *value; } /** * @brief Fake ADL based lookup function for meta pointer-like types. * @tparam Type Element type. */ template struct adl_meta_pointer_like { /** * @brief Uses the default ADL based lookup method to resolve the call. * @param value A pointer-like object. * @return The value returned from the dereferenced pointer. */ static decltype(auto) dereference(const Type &value) { return dereference_meta_pointer_like(value); } }; } // namespace entt #endif // #include "context.hpp" // #include "fwd.hpp" #ifndef ENTT_META_FWD_HPP #define ENTT_META_FWD_HPP namespace entt { class meta_sequence_container; class meta_associative_container; class meta_any; struct meta_handle; struct meta_prop; struct meta_data; struct meta_func; class meta_type; } // namespace entt #endif // #include "node.hpp" #ifndef ENTT_META_NODE_HPP #define ENTT_META_NODE_HPP #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "../core/enum.hpp" #ifndef ENTT_CORE_ENUM_HPP #define ENTT_CORE_ENUM_HPP #include namespace entt { /** * @brief Enable bitmask support for enum classes. * @tparam Type The enum type for which to enable bitmask support. */ template struct enum_as_bitmask: std::false_type {}; /*! @copydoc enum_as_bitmask */ template struct enum_as_bitmask>: std::is_enum {}; /** * @brief Helper variable template. * @tparam Type The enum class type for which to enable bitmask support. */ template inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; } // namespace entt /** * @brief Operator available for enums for which bitmask support is enabled. * @tparam Type Enum class type. * @param lhs The first value to use. * @param rhs The second value to use. * @return The result of invoking the operator on the underlying types of the * two values provided. */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator|(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) | static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator&(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) & static_cast>(rhs)); } /*! @copydoc operator| */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator^(const Type lhs, const Type rhs) noexcept { return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); } /** * @brief Operator available for enums for which bitmask support is enabled. * @tparam Type Enum class type. * @param value The value to use. * @return The result of invoking the operator on the underlying types of the * value provided. */ template [[nodiscard]] constexpr std::enable_if_t, Type> operator~(const Type value) noexcept { return static_cast(~static_cast>(value)); } /*! @copydoc operator~ */ template [[nodiscard]] constexpr std::enable_if_t, bool> operator!(const Type value) noexcept { return !static_cast>(value); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator|=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs | rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator&=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs & rhs)); } /*! @copydoc operator| */ template constexpr std::enable_if_t, Type &> operator^=(Type &lhs, const Type rhs) noexcept { return (lhs = (lhs ^ rhs)); } #endif // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" // #include "context.hpp" // #include "type_traits.hpp" #ifndef ENTT_META_TYPE_TRAITS_HPP #define ENTT_META_TYPE_TRAITS_HPP #include #include namespace entt { /** * @brief Traits class template to be specialized to enable support for meta * template information. */ template struct meta_template_traits; /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. */ template struct meta_sequence_container_traits; /** * @brief Traits class template to be specialized to enable support for meta * associative containers. */ template struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: std::false_type {}; /** * @brief Partial specialization to ensure that const pointer-like types are * also accepted. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: is_meta_pointer_like {}; /** * @brief Helper variable template. * @tparam Type Potentially pointer-like type. */ template inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; } // namespace entt #endif namespace entt { class meta_any; class meta_type; struct meta_handle; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class meta_traits : std::uint32_t { is_none = 0x0000, is_const = 0x0001, is_static = 0x0002, is_arithmetic = 0x0004, is_integral = 0x0008, is_signed = 0x0010, is_array = 0x0020, is_enum = 0x0040, is_class = 0x0080, is_meta_pointer_like = 0x0100, is_meta_sequence_container = 0x0200, is_meta_associative_container = 0x0400, _entt_enum_as_bitmask }; struct meta_type_node; struct meta_prop_node { meta_type_node (*type)(const meta_context &) noexcept {}; std::shared_ptr value{}; }; struct meta_base_node { meta_type_node (*type)(const meta_context &) noexcept {}; const void *(*cast)(const void *) noexcept {}; }; struct meta_conv_node { meta_any (*conv)(const meta_ctx &, const void *){}; }; struct meta_ctor_node { using size_type = std::size_t; size_type arity{0u}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; meta_any (*invoke)(const meta_ctx &, meta_any *const){}; }; struct meta_dtor_node { void (*dtor)(void *){}; }; struct meta_data_node { using size_type = std::size_t; meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node (*type)(const meta_context &) noexcept {}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; bool (*set)(meta_handle, meta_any){}; meta_any (*get)(const meta_ctx &, meta_handle){}; dense_map prop{}; }; struct meta_func_node { using size_type = std::size_t; meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node (*ret)(const meta_context &) noexcept {}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; std::shared_ptr next{}; dense_map prop{}; }; struct meta_template_node { using size_type = std::size_t; size_type arity{0u}; meta_type_node (*type)(const meta_context &) noexcept {}; meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; }; struct meta_type_descriptor { dense_map ctor{}; dense_map base{}; dense_map conv{}; dense_map data{}; dense_map func{}; dense_map prop{}; }; struct meta_type_node { using size_type = std::size_t; const type_info *info{}; id_type id{}; meta_traits traits{meta_traits::is_none}; size_type size_of{0u}; meta_type_node (*resolve)(const meta_context &) noexcept {}; meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; meta_any (*default_constructor)(const meta_ctx &){}; double (*conversion_helper)(void *, const void *){}; meta_any (*from_void)(const meta_ctx &, void *, const void *){}; meta_template_node templ{}; meta_dtor_node dtor{}; std::shared_ptr details{}; }; template meta_type_node resolve(const meta_context &) noexcept; template [[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { [[maybe_unused]] std::size_t pos{}; meta_type_node (*value)(const meta_context &) noexcept = nullptr; ((value = (pos++ == index ? &resolve>> : value)), ...); ENTT_ASSERT(value != nullptr, "Out of bounds"); return value(context); } [[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { if(from.info && to.info && *from.info == *to.info) { return instance; } if(from.details) { for(auto &&curr: from.details->base) { if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { return elem; } } } return nullptr; } [[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { const auto it = context.value.find(info.hash()); return it != context.value.end() ? &it->second : nullptr; } template [[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { static_assert(std::is_same_v>>, "Invalid type"); if(auto *elem = try_resolve(context, type_id()); elem) { return *elem; } meta_type_node node{ &type_id(), type_id().hash(), (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), size_of_v, &resolve, &resolve>>}; if constexpr(std::is_default_constructible_v) { node.default_constructor = +[](const meta_ctx &ctx) { return meta_any{ctx, std::in_place_type}; }; } if constexpr(std::is_arithmetic_v) { node.conversion_helper = +[](void *bin, const void *value) { return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); }; } else if constexpr(std::is_enum_v) { node.conversion_helper = +[](void *bin, const void *value) { return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); }; } if constexpr(!std::is_same_v && !std::is_function_v) { node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { if(element) { return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; } return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; }; } if constexpr(is_complete_v>) { node.templ = meta_template_node{ meta_template_traits::args_type::size, &resolve::class_type>, +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; } return node; } } // namespace internal /** * Internal details not to be documented. * @endcond */ } // namespace entt #endif // #include "range.hpp" #ifndef ENTT_META_RANGE_HPP #define ENTT_META_RANGE_HPP #include #include #include // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "context.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct meta_range_iterator final { using difference_type = std::ptrdiff_t; using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; meta_range_iterator() noexcept : it{}, ctx{} {} meta_range_iterator(const meta_ctx &area, const It iter) noexcept : it{iter}, ctx{&area} {} meta_range_iterator &operator++() noexcept { return ++it, *this; } meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } constexpr meta_range_iterator &operator--() noexcept { return --it, *this; } constexpr meta_range_iterator operator--(int) noexcept { meta_range_iterator orig = *this; return operator--(), orig; } constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr meta_range_iterator operator+(const difference_type value) const noexcept { meta_range_iterator copy = *this; return (copy += value); } constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr meta_range_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].first, Type{*ctx, it[value].second}}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->first, Type{*ctx, it->second}}; } template friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; template friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; template friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; private: It it; const meta_ctx *ctx; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs < rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. * @tparam It Type of forward iterator. */ template using meta_range = iterable_adaptor>; } // namespace entt #endif // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; /*! @brief Proxy object for sequence containers. */ class meta_sequence_container { class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_sequence_container(const meta_ctx &area = locator::value_or()) noexcept : ctx{&area} {} /** * @brief Rebinds a proxy object to a sequence container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template void rebind(any instance) noexcept { value_type_node = &internal::resolve; size_fn = &meta_sequence_container_traits::size; resize_fn = &meta_sequence_container_traits::resize; iter_fn = &meta_sequence_container_traits::iter; insert_or_erase_fn = &meta_sequence_container_traits::insert_or_erase; storage = std::move(instance); } [[nodiscard]] inline meta_type value_type() const noexcept; [[nodiscard]] inline size_type size() const noexcept; inline bool resize(const size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline iterator insert(iterator, meta_any); inline iterator erase(iterator); [[nodiscard]] inline meta_any operator[](const size_type); [[nodiscard]] inline explicit operator bool() const noexcept; private: const meta_ctx *ctx{}; internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; size_type (*size_fn)(const any &) noexcept {}; bool (*resize_fn)(any &, size_type){}; iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; iterator (*insert_or_erase_fn)(const meta_ctx &, any &, const any &, meta_any &){}; any storage{}; }; /*! @brief Proxy object for associative containers. */ class meta_associative_container { class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_associative_container(const meta_ctx &area = locator::value_or()) noexcept : ctx{&area} {} /** * @brief Rebinds a proxy object to an associative container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template void rebind(any instance) noexcept { if constexpr(!meta_associative_container_traits::key_only) { mapped_type_node = &internal::resolve; } key_only_container = meta_associative_container_traits::key_only; key_type_node = &internal::resolve; value_type_node = &internal::resolve; size_fn = &meta_associative_container_traits::size; clear_fn = &meta_associative_container_traits::clear; iter_fn = &meta_associative_container_traits::iter; insert_or_erase_fn = &meta_associative_container_traits::insert_or_erase; find_fn = &meta_associative_container_traits::find; storage = std::move(instance); } [[nodiscard]] inline bool key_only() const noexcept; [[nodiscard]] inline meta_type key_type() const noexcept; [[nodiscard]] inline meta_type mapped_type() const noexcept; [[nodiscard]] inline meta_type value_type() const noexcept; [[nodiscard]] inline size_type size() const noexcept; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline bool insert(meta_any); inline bool insert(meta_any, meta_any); inline size_type erase(meta_any); [[nodiscard]] inline iterator find(meta_any); [[nodiscard]] inline explicit operator bool() const noexcept; private: const meta_ctx *ctx{}; bool key_only_container{}; internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; size_type (*size_fn)(const any &) noexcept {}; bool (*clear_fn)(any &){}; iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; size_type (*insert_or_erase_fn)(any &, meta_any &, meta_any &){}; iterator (*find_fn)(const meta_ctx &, any &, meta_any &){}; any storage{}; }; /*! @brief Opaque wrapper for values of any type. */ class meta_any { enum class operation : std::uint8_t { deref, seq, assoc }; using vtable_type = void(const operation, const any &, void *); template static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) { static_assert(std::is_same_v>, Type>, "Invalid type"); if constexpr(!std::is_void_v) { switch(op) { case operation::deref: if constexpr(is_meta_pointer_like_v) { if constexpr(std::is_function_v::element_type>) { static_cast(other)->emplace(any_cast(value)); } else if constexpr(!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(value))); if constexpr(std::is_constructible_v) { if(const auto &pointer_like = any_cast(value); pointer_like) { static_cast(other)->emplace(adl_meta_pointer_like::dereference(pointer_like)); } } else { static_cast(other)->emplace(adl_meta_pointer_like::dereference(any_cast(value))); } } } break; case operation::seq: if constexpr(is_complete_v>) { static_cast(other)->rebind(std::move(const_cast(value))); } break; case operation::assoc: if constexpr(is_complete_v>) { static_cast(other)->rebind(std::move(const_cast(value))); } break; } } } void release() { if(node.dtor.dtor && owner()) { node.dtor.dtor(storage.data()); } } meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept : storage{std::move(ref)}, ctx{&area}, node{storage ? other.node : internal::meta_type_node{}}, vtable{storage ? other.vtable : &basic_vtable} {} public: /*! Default constructor. */ meta_any() noexcept : meta_any{meta_ctx_arg, locator::value_or()} {} /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept : storage{}, ctx{&area}, node{}, vtable{&basic_vtable} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit meta_any(std::in_place_type_t, Args &&...args) : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param area The context from which to search for meta types. * @param args Parameters to use to construct the instance. */ template explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) : storage{std::in_place_type, std::forward(args)...}, ctx{&area}, node{internal::resolve>>(internal::meta_context::from(*ctx))}, vtable{&basic_vtable>>} {} /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, meta_any>>> meta_any(Type &&value) : meta_any{locator::value_or(), std::forward(value)} {} /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param area The context from which to search for meta types. * @param value An instance of an object to use to initialize the wrapper. */ template, meta_any>>> meta_any(const meta_ctx &area, Type &&value) : meta_any{area, std::in_place_type>, std::forward(value)} {} /** * @brief Context aware copy constructor. * @param area The context from which to search for meta types. * @param other The instance to copy from. */ meta_any(const meta_ctx &area, const meta_any &other) : meta_any{other} { ctx = &area; node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; } /** * @brief Context aware move constructor. * @param area The context from which to search for meta types. * @param other The instance to move from. */ meta_any(const meta_ctx &area, meta_any &&other) : meta_any{std::move(other)} { ctx = &area; node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; } /** * @brief Copy constructor. * @param other The instance to copy from. */ meta_any(const meta_any &other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ meta_any(meta_any &&other) noexcept : storage{std::move(other.storage)}, ctx{other.ctx}, node{std::exchange(other.node, internal::meta_type_node{})}, vtable{std::exchange(other.vtable, &basic_vtable)} {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { release(); } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This meta any object. */ meta_any &operator=(const meta_any &other) { release(); storage = other.storage; ctx = other.ctx; node = other.node; vtable = other.vtable; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This meta any object. */ meta_any &operator=(meta_any &&other) noexcept { release(); storage = std::move(other.storage); ctx = other.ctx; node = std::exchange(other.node, internal::meta_type_node{}); vtable = std::exchange(other.vtable, &basic_vtable); return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This meta any object. */ template std::enable_if_t, meta_any>, meta_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /*! @copydoc any::type */ [[nodiscard]] inline meta_type type() const noexcept; /*! @copydoc any::data */ [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc any::data */ [[nodiscard]] void *data() noexcept { return storage.data(); } /** * @brief Invokes the underlying function, if possible. * @tparam Args Types of arguments to use to invoke the function. * @param id Unique identifier. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, Args &&...args) const; /*! @copydoc invoke */ template meta_any invoke(const id_type id, Args &&...args); /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param id Unique identifier. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, Type &&value); /** * @brief Gets the value of a given variable. * @param id Unique identifier. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id) const; /*! @copydoc get */ [[nodiscard]] meta_any get(const id_type id); /** * @brief Tries to cast an instance to a given type. * @tparam Type Type to which to cast the instance. * @return A (possibly null) pointer to the contained instance. */ template [[nodiscard]] const Type *try_cast() const { const auto other = internal::resolve>(internal::meta_context::from(*ctx)); return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); } /*! @copydoc try_cast */ template [[nodiscard]] Type *try_cast() { if constexpr(std::is_const_v) { return std::as_const(*this).try_cast>(); } else { const auto other = internal::resolve>(internal::meta_context::from(*ctx)); return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); } } /** * @brief Tries to cast an instance to a given type. * * @warning * Attempting to perform an invalid cast results is undefined behavior. * * @tparam Type Type to which to cast the instance. * @return A reference to the contained instance. */ template [[nodiscard]] Type cast() const { auto *const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc cast */ template [[nodiscard]] Type cast() { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /** * @brief Converts an object in such a way that a given cast becomes viable. * @param type Meta type to which the cast is requested. * @return A valid meta any object if there exists a viable conversion, an * invalid one otherwise. */ [[nodiscard]] meta_any allow_cast(const meta_type &type) const; /** * @brief Converts an object in such a way that a given cast becomes viable. * @param type Meta type to which the cast is requested. * @return True if there exists a viable conversion, false otherwise. */ [[nodiscard]] bool allow_cast(const meta_type &type) { if(auto other = std::as_const(*this).allow_cast(type); other) { if(other.owner()) { std::swap(*this, other); } return true; } return false; } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return A valid meta any object if there exists a viable conversion, an * invalid one otherwise. */ template [[nodiscard]] meta_any allow_cast() const { if constexpr(std::is_reference_v && !std::is_const_v>) { return meta_any{meta_ctx_arg, *ctx}; } else { auto other = internal::resolve>>(internal::meta_context::from(*ctx)); return allow_cast(meta_type{*ctx, other}); } } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return True if there exists a viable conversion, false otherwise. */ template bool allow_cast() { auto other = internal::resolve>>(internal::meta_context::from(*ctx)); return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); } /*! @copydoc any::emplace */ template void emplace(Args &&...args) { release(); storage.emplace(std::forward(args)...); node = internal::resolve>>(internal::meta_context::from(*ctx)); vtable = &basic_vtable>>; } /*! @copydoc any::assign */ bool assign(const meta_any &other); /*! @copydoc any::assign */ bool assign(meta_any &&other); /*! @copydoc any::reset */ void reset() { release(); storage.reset(); node = {}; vtable = &basic_vtable; } /** * @brief Returns a sequence container proxy. * @return A sequence container proxy for the underlying object. */ [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { any detached = storage.as_ref(); meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /*! @copydoc as_sequence_container */ [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { any detached = storage.as_ref(); meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /** * @brief Returns an associative container proxy. * @return An associative container proxy for the underlying object. */ [[nodiscard]] meta_associative_container as_associative_container() noexcept { any detached = storage.as_ref(); meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /*! @copydoc as_associative_container */ [[nodiscard]] meta_associative_container as_associative_container() const noexcept { any detached = storage.as_ref(); meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /** * @brief Indirection operator for dereferencing opaque objects. * @return A wrapper that shares a reference to an unmanaged object if the * wrapped element is dereferenceable, an invalid meta any otherwise. */ [[nodiscard]] meta_any operator*() const noexcept { meta_any ret{meta_ctx_arg, *ctx}; vtable(operation::deref, storage, &ret); return ret; } /** * @brief Returns false if a wrapper is invalid, true otherwise. * @return False if the wrapper is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return !(node.info == nullptr); } /*! @copydoc any::operator== */ [[nodiscard]] bool operator==(const meta_any &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); } /*! @copydoc any::operator!= */ [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { return !(*this == other); } /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() noexcept { return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() const noexcept { return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::owner */ [[nodiscard]] bool owner() const noexcept { return storage.owner(); } private: any storage; const meta_ctx *ctx; internal::meta_type_node node; vtable_type *vtable; }; /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @param ctx The context from which to search for meta types. * @return A properly initialized and not necessarily owning wrapper. */ template meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { return meta_any{ctx, std::in_place_type, std::forward(value)}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template meta_any forward_as_meta(Type &&value) { return forward_as_meta(locator::value_or(), std::forward(value)); } /** * @brief Opaque pointers to instances of any type. * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
* Handles are used to generate references to actual objects when needed. */ struct meta_handle { /*! Default constructor. */ meta_handle() noexcept : meta_handle{meta_ctx_arg, locator::value_or()} {} /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept : any{meta_ctx_arg, area} {} /** * @brief Creates a handle that points to an unmanaged object. * @param value An instance of an object to use to initialize the handle. */ meta_handle(meta_any &value) noexcept : any{value.as_ref()} {} /** * @brief Creates a handle that points to an unmanaged object. * @param value An instance of an object to use to initialize the handle. */ meta_handle(const meta_any &value) noexcept : any{value.as_ref()} {} /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param ctx The context from which to search for meta types. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(const meta_ctx &ctx, Type &value) noexcept : any{ctx, std::in_place_type, value} {} /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(Type &value) noexcept : meta_handle{locator::value_or(), value} {} /** * @brief Context aware copy constructor. * @param area The context from which to search for meta types. * @param other The instance to copy from. */ meta_handle(const meta_ctx &area, const meta_handle &other) : any{area, other.any} {} /** * @brief Context aware move constructor. * @param area The context from which to search for meta types. * @param other The instance to move from. */ meta_handle(const meta_ctx &area, meta_handle &&other) : any{area, std::move(other.any)} {} /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle &) = delete; /*! @brief Default move constructor. */ meta_handle(meta_handle &&) = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This meta handle. */ meta_handle &operator=(const meta_handle &) = delete; /** * @brief Default move assignment operator. * @return This meta handle. */ meta_handle &operator=(meta_handle &&) = default; /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(any); } /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any *operator->() { return &any; } /*! @copydoc operator-> */ [[nodiscard]] const meta_any *operator->() const { return &any; } private: meta_any any; }; /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Default constructor. */ meta_prop() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_prop_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_data_node::size_type; /*! @brief Default constructor. */ meta_data() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the number of setters available. * @return The number of setters available. */ [[nodiscard]] size_type arity() const noexcept { return node->arity; } /** * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ [[nodiscard]] bool is_const() const noexcept { return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ [[nodiscard]] bool is_static() const noexcept { return static_cast(node->traits & internal::meta_traits::is_static); } /*! @copydoc meta_any::type */ [[nodiscard]] inline meta_type type() const noexcept; /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(meta_handle instance, Type &&value) const { return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); } /** * @brief Gets the value of a given variable. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); } /** * @brief Returns the type accepted by the i-th setter. * @param index Index of the setter of which to return the accepted type. * @return The type accepted by the i-th setter. */ [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Returns a range to visit registered meta properties. * @return An iterable range to visit registered meta properties. */ [[nodiscard]] meta_range prop() const noexcept { return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { const auto it = node->prop.find(key); return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_data_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_func_node::size_type; /*! @brief Default constructor. */ meta_func() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ [[nodiscard]] size_type arity() const noexcept { return node->arity; } /** * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ [[nodiscard]] bool is_const() const noexcept { return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ [[nodiscard]] bool is_static() const noexcept { return static_cast(node->traits & internal::meta_traits::is_static); } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ [[nodiscard]] inline meta_type ret() const noexcept; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Invokes the underlying function, if possible. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(meta_handle instance, Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /*! @copydoc meta_data::prop */ [[nodiscard]] meta_range prop() const noexcept { return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { const auto it = node->prop.find(key); return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns the next overload of a given function, if any. * @return The next overload of the given function, if any. */ [[nodiscard]] meta_func next() const { return node->next ? meta_func{*ctx, *node->next} : meta_func{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_func_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for types. */ class meta_type { template [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { decltype(next()) candidate = nullptr; size_type same{}; bool ambiguous{}; for(auto curr = next(); curr; curr = next()) { if constexpr(std::is_same_v, internal::meta_func_node>) { if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { continue; } } if(curr->arity == sz) { size_type match{}; size_type pos{}; for(; pos < sz && args[pos]; ++pos) { const auto other = curr->arg(*ctx, pos); const auto type = args[pos].type(); if(const auto &info = other.info(); info == type.info()) { ++match; } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { break; } } if(pos == sz) { if(!candidate || match > same) { candidate = curr; same = match; ambiguous = false; } else if(match == same) { if constexpr(std::is_same_v, internal::meta_func_node>) { if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; ambiguous = false; continue; } } ambiguous = true; } } } } return ambiguous ? nullptr : candidate; } public: /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_type_node::size_type; /*! @brief Default constructor. */ meta_type() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept : node{curr}, ctx{&area} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept : meta_type{area, curr.type(internal::meta_context::from(area))} {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ [[nodiscard]] const type_info &info() const noexcept { return *node.info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ [[nodiscard]] id_type id() const noexcept { return node.id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ [[nodiscard]] size_type size_of() const noexcept { return node.size_of; } /** * @brief Checks whether a type refers to an arithmetic type or not. * @return True if the underlying type is an arithmetic type, false * otherwise. */ [[nodiscard]] bool is_arithmetic() const noexcept { return static_cast(node.traits & internal::meta_traits::is_arithmetic); } /** * @brief Checks whether a type refers to an integral type or not. * @return True if the underlying type is an integral type, false otherwise. */ [[nodiscard]] bool is_integral() const noexcept { return static_cast(node.traits & internal::meta_traits::is_integral); } /** * @brief Checks whether a type refers to a signed type or not. * @return True if the underlying type is a signed type, false otherwise. */ [[nodiscard]] bool is_signed() const noexcept { return static_cast(node.traits & internal::meta_traits::is_signed); } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ [[nodiscard]] bool is_array() const noexcept { return static_cast(node.traits & internal::meta_traits::is_array); } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ [[nodiscard]] bool is_enum() const noexcept { return static_cast(node.traits & internal::meta_traits::is_enum); } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ [[nodiscard]] bool is_class() const noexcept { return static_cast(node.traits & internal::meta_traits::is_class); } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ [[nodiscard]] bool is_pointer() const noexcept { return node.info && (node.info->hash() != remove_pointer().info().hash()); } /** * @brief Provides the type for which the pointer is defined. * @return The type for which the pointer is defined or this type if it * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const noexcept { return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; } /** * @brief Checks whether a type is a pointer-like type or not. * @return True if the underlying type is a pointer-like one, false * otherwise. */ [[nodiscard]] bool is_pointer_like() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); } /** * @brief Checks whether a type refers to a recognized class template * specialization or not. * @return True if the type is a recognized class template specialization, * false otherwise. */ [[nodiscard]] bool is_template_specialization() const noexcept { return (node.templ.arity != 0u); } /** * @brief Returns the number of template arguments. * @return The number of template arguments. */ [[nodiscard]] size_type template_arity() const noexcept { return node.templ.arity; } /** * @brief Returns a tag for the class template of the underlying type. * @return The tag for the class template of the underlying type. */ [[nodiscard]] inline meta_type template_type() const noexcept { return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the type of the i-th template argument of a type. * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; } /** * @brief Returns a range to visit registered top-level base meta types. * @return An iterable range to visit registered top-level base meta types. */ [[nodiscard]] meta_range base() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; } /** * @brief Returns a range to visit registered top-level meta data. * @return An iterable range to visit registered top-level meta data. */ [[nodiscard]] meta_range data() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; } /** * @brief Lookup utility for meta data (bases are also visited). * @param id Unique identifier. * @return The registered meta data for the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { if(node.details) { if(const auto it = node.details->data.find(id); it != node.details->data.cend()) { return meta_data{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.data(id); elem) { return elem; } } return meta_data{}; } /** * @brief Returns a range to visit registered top-level functions. * @return An iterable range to visit registered top-level functions. */ [[nodiscard]] meta_range func() const noexcept { using return_type = meta_range; return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; } /** * @brief Lookup utility for meta functions (bases are also visited). * * In case of overloaded functions, the first one with the required * identifier is returned. * * @param id Unique identifier. * @return The registered meta function for the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { if(node.details) { if(const auto it = node.details->func.find(id); it != node.details->func.cend()) { return meta_func{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.func(id); elem) { return elem; } } return meta_func{}; } /** * @brief Creates an instance of the underlying type, if possible. * * If suitable, the implicitly generated default constructor is used. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { if(node.details) { if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { return candidate->invoke(*ctx, args); } } if(sz == 0u && node.default_constructor) { return node.default_constructor(*ctx); } return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief construct * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return construct(arguments, sizeof...(Args)); } /** * @brief Wraps an opaque element of the underlying type. * @param element A valid pointer to an element of the underlying type. * @return A wrapper that references the given instance. */ meta_any from_void(void *element) const { return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; } /*! @copydoc from_void */ meta_any from_void(const void *element) const { return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Invokes a function given an identifier, if possible. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { if(node.details) { if(auto it = node.details->func.find(id); it != node.details->func.cend()) { if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); } } } for(auto &&curr: base()) { if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { return elem; } } return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, meta_handle instance, Type &&value) const { const auto candidate = data(id); return candidate && candidate.set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns a range to visit registered top-level meta properties. * @return An iterable range to visit registered top-level meta properties. */ [[nodiscard]] meta_range prop() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; } /** * @brief Lookup utility for meta properties (bases are also visited). * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { if(node.details) { if(const auto it = node.details->prop.find(key); it != node.details->prop.cend()) { return meta_prop{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.prop(key); elem) { return elem; } } return meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return !(ctx == nullptr); } /** * @brief Checks if two objects refer to the same type. * @param other The object with which to compare. * @return True if the objects refer to the same type, false otherwise. */ [[nodiscard]] bool operator==(const meta_type &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); } private: internal::meta_type_node node; const meta_ctx *ctx; }; /** * @brief Checks if two objects refer to the same type. * @param lhs An object, either valid or not. * @param rhs An object, either valid or not. * @return False if the objects refer to the same node, true otherwise. */ [[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { return !(lhs == rhs); } [[nodiscard]] inline meta_type meta_any::type() const noexcept { return node.info ? meta_type{*ctx, node} : meta_type{}; } template meta_any meta_any::invoke(const id_type id, Args &&...args) const { return type().invoke(id, *this, std::forward(args)...); } template meta_any meta_any::invoke(const id_type id, Args &&...args) { return type().invoke(id, *this, std::forward(args)...); } template bool meta_any::set(const id_type id, Type &&value) { return type().set(id, *this, std::forward(value)); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) const { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { if(node.info && *node.info == type.info()) { return as_ref(); } if(const auto *value = data(); node.details) { if(auto it = node.details->conv.find(type.info().hash()); it != node.details->conv.cend()) { return it->second.conv(*ctx, data()); } for(auto &&curr: node.details->base) { const auto &as_const = curr.second.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.second.cast(value)); if(auto other = as_const.allow_cast(type); other) { return other; } } } if(node.conversion_helper && (type.is_arithmetic() || type.is_enum())) { // exploits the fact that arithmetic types and enums are also default constructible auto other = type.construct(); ENTT_ASSERT(other.node.conversion_helper, "Conversion helper not found"); const auto value = node.conversion_helper(nullptr, storage.data()); other.node.conversion_helper(other.storage.data(), &value); return other; } return meta_any{meta_ctx_arg, *ctx}; } inline bool meta_any::assign(const meta_any &other) { auto value = other.allow_cast({*ctx, node}); return value && storage.assign(std::move(value.storage)); } inline bool meta_any::assign(meta_any &&other) { if(*node.info == *other.node.info) { return storage.assign(std::move(other.storage)); } return assign(std::as_const(other)); } [[nodiscard]] inline meta_type meta_data::type() const noexcept { return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; } [[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { return index < arity() ? node->arg(*ctx, index) : meta_type{}; } [[nodiscard]] inline meta_type meta_func::ret() const noexcept { return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; } [[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { return index < arity() ? node->arg(*ctx, index) : meta_type{}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ class meta_sequence_container::meta_iterator final { friend class meta_sequence_container; enum class operation : std::uint8_t { incr, deref }; using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *); template static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) { switch(op) { case operation::incr: { auto &it = any_cast(const_cast(value)); it = std::next(it, offset); } break; case operation::deref: { const auto &it = any_cast(value); other->emplace(*it); } break; } } public: using difference_type = std::ptrdiff_t; using value_type = meta_any; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr meta_iterator() noexcept : ctx{}, vtable{}, handle{} {} template explicit meta_iterator(const meta_ctx &area, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, handle{std::move(iter)} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, 1, nullptr); return *this; } meta_iterator operator++(int value) noexcept { meta_iterator orig = *this; vtable(operation::incr, handle, ++value, nullptr); return orig; } meta_iterator &operator--() noexcept { vtable(operation::incr, handle, -1, nullptr); return *this; } meta_iterator operator--(int value) noexcept { meta_iterator orig = *this; vtable(operation::incr, handle, --value, nullptr); return orig; } [[nodiscard]] reference operator*() const { reference other{meta_ctx_arg, *ctx}; vtable(operation::deref, handle, 0, &other); return other; } [[nodiscard]] pointer operator->() const { return operator*(); } [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: const meta_ctx *ctx; vtable_type *vtable; any handle; }; class meta_associative_container::meta_iterator final { enum class operation : std::uint8_t { incr, deref }; using vtable_type = void(const operation, const any &, std::pair *); template static void basic_vtable(const operation op, const any &value, std::pair *other) { switch(op) { case operation::incr: ++any_cast(const_cast(value)); break; case operation::deref: const auto &it = any_cast(value); if constexpr(KeyOnly) { other->first.emplace(*it); } else { other->first.emplacefirst))>(it->first); other->second.emplacesecond))>(it->second); } break; } } public: using difference_type = std::ptrdiff_t; using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr meta_iterator() noexcept : ctx{}, vtable{}, handle{} {} template meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, handle{std::move(iter)} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); return *this; } meta_iterator operator++(int) noexcept { meta_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const { reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; vtable(operation::deref, handle, &other); return other; } [[nodiscard]] pointer operator->() const { return operator*(); } [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: const meta_ctx *ctx; vtable_type *vtable; any handle; }; /** * Internal details not to be documented. * @endcond */ /** * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ [[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the size of a container. * @return The size of the container. */ [[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { return size_fn(storage); } /** * @brief Resizes a container to contain a given number of elements. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::resize(const size_type sz) { return resize_fn(storage, sz); } /** * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { return resize_fn(storage, 0u); } /** * @brief Returns an iterator to the first element of a container. * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { return iter_fn(*ctx, storage, false); } /** * @brief Returns an iterator that is past the last element of a container. * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { return iter_fn(*ctx, storage, true); } /** * @brief Inserts an element at a specified location of a container. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A possibly invalid iterator to the inserted element. */ inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { return insert_or_erase_fn(*ctx, storage, it.handle, value); } /** * @brief Removes a given element from a container. * @param it Iterator to the element to remove. * @return A possibly invalid iterator following the last removed element. */ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { return insert(std::move(it), {}); } /** * @brief Returns a reference to the element at a given location of a container * (no bounds checking is performed). * @param pos The position of the element to return. * @return A reference to the requested element properly wrapped. */ [[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { auto it = begin(); it.operator++(static_cast(pos) - 1); return *it; } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { return static_cast(storage); } /** * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { return key_only_container; } /** * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::value_type */ [[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { return iter_fn(*ctx, storage, false); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { return iter_fn(*ctx, storage, true); } /** * @brief Inserts a key only element into a container. * @param key The key of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key) { meta_any value{*ctx, std::in_place_type}; return (insert_or_erase_fn(storage, key, value) != 0u); } /** * @brief Inserts a key/value element into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key, meta_any value) { return (insert_or_erase_fn(storage, key, value) != 0u); } /** * @brief Removes the specified element from a container. * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { return insert(std::move(key), meta_any{meta_ctx_arg, *ctx}); } /** * @brief Returns an iterator to the element with a given key, if any. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { return find_fn(*ctx, storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const noexcept { return static_cast(storage); } } // namespace entt #endif // #include "type_traits.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct is_dynamic_sequence_container: std::false_type {}; template struct is_dynamic_sequence_container>: std::true_type {}; template struct is_key_only_meta_associative_container: std::true_type {}; template struct is_key_only_meta_associative_container>: std::false_type {}; template struct basic_meta_sequence_container_traits { using iterator = meta_sequence_container::iterator; using size_type = std::size_t; [[nodiscard]] static size_type size(const any &container) noexcept { return any_cast(container).size(); } [[nodiscard]] static bool resize([[maybe_unused]] any &container, [[maybe_unused]] size_type sz) { if constexpr(is_dynamic_sequence_container::value) { if(auto *const cont = any_cast(&container); cont) { cont->resize(sz); return true; } } return false; } [[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) { if(auto *const cont = any_cast(&container); cont) { return iterator{ctx, as_end ? cont->end() : cont->begin()}; } const Type &as_const = any_cast(container); return iterator{ctx, as_end ? as_const.end() : as_const.begin()}; } [[nodiscard]] static iterator insert_or_erase([[maybe_unused]] const meta_ctx &ctx, [[maybe_unused]] any &container, [[maybe_unused]] const any &handle, [[maybe_unused]] meta_any &value) { if constexpr(is_dynamic_sequence_container::value) { if(auto *const cont = any_cast(&container); cont) { typename Type::const_iterator it{}; if(auto *non_const = any_cast(&handle); non_const) { it = *non_const; } else { it = any_cast(handle); } if(value) { // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector if(value.allow_cast() || value.allow_cast()) { const auto *element = value.try_cast>(); return iterator{ctx, cont->insert(it, element ? *element : value.cast())}; } } else { return iterator{ctx, cont->erase(it)}; } } } return iterator{}; } }; template struct basic_meta_associative_container_traits { using iterator = meta_associative_container::iterator; using size_type = std::size_t; static constexpr auto key_only = is_key_only_meta_associative_container::value; [[nodiscard]] static size_type size(const any &container) noexcept { return any_cast(container).size(); } [[nodiscard]] static bool clear(any &container) { if(auto *const cont = any_cast(&container); cont) { cont->clear(); return true; } return false; } [[nodiscard]] static iterator iter(const meta_ctx &ctx, any &container, const bool as_end) { if(auto *const cont = any_cast(&container); cont) { return iterator{ctx, std::bool_constant{}, as_end ? cont->end() : cont->begin()}; } const auto &as_const = any_cast(container); return iterator{ctx, std::bool_constant{}, as_end ? as_const.end() : as_const.begin()}; } [[nodiscard]] static size_type insert_or_erase(any &container, meta_any &key, meta_any &value) { if(auto *const cont = any_cast(&container); cont && key.allow_cast()) { if(value) { if constexpr(key_only) { return cont->insert(key.cast()).second; } else { return value.allow_cast() && cont->emplace(key.cast(), value.cast()).second; } } else { return cont->erase(key.cast()); } } return 0u; } [[nodiscard]] static iterator find(const meta_ctx &ctx, any &container, meta_any &key) { if(key.allow_cast()) { if(auto *const cont = any_cast(&container); cont) { return iterator{ctx, std::bool_constant{}, cont->find(key.cast())}; } return iterator{ctx, std::bool_constant{}, any_cast(container).find(key.cast())}; } return iterator{}; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Meta sequence container traits for `std::vector`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; /** * @brief Meta sequence container traits for `std::array`s of any type. * @tparam Type Template arguments for the container. * @tparam N Template arguments for the container. */ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; /** * @brief Meta sequence container traits for `std::list`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; /** * @brief Meta sequence container traits for `std::deque`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_sequence_container_traits> : internal::basic_meta_sequence_container_traits> {}; /** * @brief Meta associative container traits for `std::map`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::unordered_map`s of any * type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::set`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `std::unordered_set`s of any * type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `dense_map`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; /** * @brief Meta associative container traits for `dense_set`s of any type. * @tparam Args Template arguments for the container. */ template struct meta_associative_container_traits> : internal::basic_meta_associative_container_traits> {}; } // namespace entt #endif // #include "meta/context.hpp" #ifndef ENTT_META_CTX_HPP #define ENTT_META_CTX_HPP // #include "../container/dense_map.hpp" // #include "../core/fwd.hpp" // #include "../core/utility.hpp" namespace entt { class meta_ctx; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct meta_type_node; struct meta_context { dense_map value{}; static inline meta_context &from(meta_ctx &ctx); static inline const meta_context &from(const meta_ctx &ctx); }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @brief Disambiguation tag for constructors and the like. */ class meta_ctx_arg_t final {}; /*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ inline constexpr meta_ctx_arg_t meta_ctx_arg{}; /*! @brief Opaque meta context type. */ class meta_ctx: private internal::meta_context { /*! @brief Attorney idiom like model to access the base class. */ friend struct internal::meta_context; }; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { return ctx; } inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { return ctx; } /** * Internal details not to be documented. * @endcond */ } // namespace entt #endif // #include "meta/factory.hpp" #ifndef ENTT_META_FACTORY_HPP #define ENTT_META_FACTORY_HPP #include #include #include #include #include #include // #include "../config/config.h" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../locator/locator.hpp" // #include "context.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "policy.hpp" #ifndef ENTT_META_POLICY_HPP #define ENTT_META_POLICY_HPP #include namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ struct as_ref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = std::is_reference_v && !std::is_const_v>; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as cref_ policy. */ struct as_cref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = std::is_reference_v; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as-is_ policy. */ struct as_is_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = true; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as void_ policy. */ struct as_void_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = true; /** * Internal details not to be documented. * @endcond */ }; /** * @brief Provides the member constant `value` to true if a type also is a meta * policy, false otherwise. * @tparam Type Type to check. */ template struct is_meta_policy : std::disjunction< std::is_same, std::is_same, std::is_same, std::is_same> {}; /** * @brief Helper variable template. * @tparam Type Type to check. */ template inline constexpr bool is_meta_policy_v = is_meta_policy::value; } // namespace entt #endif // #include "range.hpp" // #include "resolve.hpp" #ifndef ENTT_META_RESOLVE_HPP #define ENTT_META_RESOLVE_HPP #include // #include "../core/type_info.hpp" // #include "../locator/locator.hpp" // #include "context.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "range.hpp" namespace entt { /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. * @param ctx The context from which to search for meta types. * @return The meta type associated with the given type, if any. */ template [[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { auto &&context = internal::meta_context::from(ctx); return {ctx, internal::resolve>>(context)}; } /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. * @return The meta type associated with the given type, if any. */ template [[nodiscard]] meta_type resolve() noexcept { return resolve(locator::value_or()); } /** * @brief Returns a range to use to visit all meta types. * @param ctx The context from which to search for meta types. * @return An iterable range to use to visit all meta types. */ [[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { auto &&context = internal::meta_context::from(ctx); return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; } /** * @brief Returns a range to use to visit all meta types. * @return An iterable range to use to visit all meta types. */ [[nodiscard]] inline meta_range resolve() noexcept { return resolve(locator::value_or()); } /** * @brief Returns the meta type associated with a given identifier, if any. * @param ctx The context from which to search for meta types. * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ [[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { for(auto &&curr: resolve(ctx)) { if(curr.second.id() == id) { return curr.second; } } return meta_type{}; } /** * @brief Returns the meta type associated with a given identifier, if any. * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ [[nodiscard]] inline meta_type resolve(const id_type id) noexcept { return resolve(locator::value_or(), id); } /** * @brief Returns the meta type associated with a given type info object. * @param ctx The context from which to search for meta types. * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ [[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { auto &&context = internal::meta_context::from(ctx); const auto *elem = internal::try_resolve(context, info); return elem ? meta_type{ctx, *elem} : meta_type{}; } /** * @brief Returns the meta type associated with a given type info object. * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ [[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { return resolve(locator::value_or(), info); } } // namespace entt #endif // #include "utility.hpp" #ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP #include #include #include #include // #include "../core/type_traits.hpp" // #include "../locator/locator.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "policy.hpp" namespace entt { /** * @brief Meta function descriptor traits. * @tparam Ret Function return type. * @tparam Args Function arguments. * @tparam Static Function staticness. * @tparam Const Function constness. */ template struct meta_function_descriptor_traits { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = Args; /*! @brief True if the meta function is static, false otherwise. */ static constexpr bool is_static = Static; /*! @brief True if the meta function is const, false otherwise. */ static constexpr bool is_const = Const; }; /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, true> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta data is associated. * @tparam Class Actual owner of the data member. * @tparam Ret Data member type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret &, std::conditional_t, type_list<>, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam MaybeType First function argument. * @tparam Args Other function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t>, Type>, type_list, type_list>, !std::is_base_of_v>, Type>, std::is_base_of_v>, Type> && std::is_const_v>> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, type_list<>, true, false> {}; /** * @brief Meta function helper. * * Converts a function type to be associated with a reflected type into its meta * function descriptor. * * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template class meta_function_helper { template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Class); public: /*! @brief The meta function descriptor of the given function. */ using type = decltype(get_rid_of_noexcept(std::declval())); }; /** * @brief Helper type. * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template using meta_function_helper_t = typename meta_function_helper::type; /** * @brief Wraps a value depending on the given policy. * * This function always returns a wrapped value in the requested context.
* Therefore, if the passed value is itself a wrapped object with a different * context, it undergoes a rebinding to the requested context. * * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param ctx The context from which to search for meta types. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type}; } else if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type, value}; } else if constexpr(std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; } else { return meta_any{ctx, std::forward(value)}; } } /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(Type &&value) { return meta_dispatch(locator::value_or(), std::forward(value)); } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param ctx The context from which to search for meta types. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { auto &&context = internal::meta_context::from(ctx); return {ctx, internal::meta_arg_node(context, Type{}, index)}; } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { return meta_arg(locator::value_or(), index); } /** * @brief Sets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to set. * @param instance An opaque instance of the underlying type, if required. * @param value Parameter to use to set the variable. * @return True in case of success, false otherwise. */ template [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { if constexpr(!std::is_same_v && !std::is_same_v) { if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { using descriptor = meta_function_helper_t; using data_type = type_list_element_t; if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { std::invoke(Data, *clazz, value.cast()); return true; } } else if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t::return_type>; if constexpr(!std::is_array_v && !std::is_const_v) { if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { std::invoke(Data, *clazz) = value.cast(); return true; } } } else { using data_type = std::remove_reference_t; if constexpr(!std::is_array_v && !std::is_const_v) { if(value.allow_cast()) { *Data = value.cast(); return true; } } } } return false; } /** * @brief Gets the value of a given variable. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { if constexpr(std::is_member_pointer_v || std::is_function_v>>) { if constexpr(!std::is_array_v>>>) { if constexpr(std::is_invocable_v) { if(auto *clazz = instance->try_cast(); clazz) { return meta_dispatch(ctx, std::invoke(Data, *clazz)); } } if constexpr(std::is_invocable_v) { if(auto *fallback = instance->try_cast(); fallback) { return meta_dispatch(ctx, std::invoke(Data, *fallback)); } } } return meta_any{meta_ctx_arg, ctx}; } else if constexpr(std::is_pointer_v) { if constexpr(std::is_array_v>) { return meta_any{meta_ctx_arg, ctx}; } else { return meta_dispatch(ctx, *Data); } } else { return meta_dispatch(ctx, Data); } } /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { return meta_getter(locator::value_or(), std::move(instance)); } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { if constexpr(std::is_same_v(candidate), args...)), void>) { std::invoke(std::forward(candidate), args...); return meta_any{ctx, std::in_place_type}; } else { return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); } } template [[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { using descriptor = meta_function_helper_t>; if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else { if(((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); } } return meta_any{meta_ctx_arg, ctx}; } template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { if(((args + Index)->allow_cast() && ...)) { return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; } return meta_any{meta_ctx_arg, ctx}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Tries to _invoke_ an object given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to _invoke_ an object given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); } /** * @brief Tries to invoke a function given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to invoke a function given a list of erased parameters. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @param ctx The context from which to search for meta types. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { return internal::meta_construct(ctx, args, std::index_sequence_for{}); } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(meta_any *const args) { return meta_construct(locator::value_or(), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param ctx The context from which to search for meta types. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } else { return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); } } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { return meta_construct(locator::value_or(), std::forward(candidate), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { return meta_construct(ctx, Candidate, args); } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { return meta_construct(locator::value_or(), args); } } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { auto &&context = internal::meta_context::from(ctx); ENTT_ASSERT(context.value.contains(info.hash()), "Type not available"); return context.value[info.hash()]; } inline meta_base_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_base_node node) { return parent.details->base.insert_or_assign(id, std::move(node)).first->second; } inline meta_conv_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_conv_node node) { return parent.details->conv.insert_or_assign(id, std::move(node)).first->second; } inline meta_ctor_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_ctor_node node) { return parent.details->ctor.insert_or_assign(id, std::move(node)).first->second; } inline meta_dtor_node &meta_extend(internal::meta_type_node &parent, meta_dtor_node node) { return (parent.dtor = std::move(node)); } inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) { return parent.details->data.insert_or_assign(id, std::move(node)).first->second; } inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) { if(auto it = parent.details->func.find(id); it != parent.details->func.end()) { for(auto *curr = &it->second; curr; curr = curr->next.get()) { if(curr->invoke == node.invoke) { node.next = std::move(curr->next); *curr = std::move(node); return *curr; } } // locally overloaded function node.next = std::make_shared(std::move(parent.details->func[id])); } return parent.details->func.insert_or_assign(id, std::move(node)).first->second; } inline meta_prop_node &meta_extend(dense_map &prop, const id_type id, meta_prop_node node) { return (prop[id] = std::move(node)); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic meta factory to be used for reflection purposes. * @tparam Type Reflected type for which the factory was created. */ template class meta_factory { template void data(const id_type id, std::index_sequence) noexcept { using data_type = std::invoke_result_t; using args_type = type_list)>::args_type...>; static_assert(Policy::template value, "Invalid return type for the given policy"); auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ /* this is never static */ (std::is_member_object_pointer_v)> && ... && std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, Setter::size, &internal::resolve>>, &meta_arg::size != 1u, type_list_element_t>...>>, +[](meta_handle instance, meta_any value) { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, &meta_getter}); bucket = &elem.prop; } public: /*! @brief Default constructor. */ meta_factory() noexcept : meta_factory{locator::value_or()} {} /** * @brief Context aware constructor. * @param area The context into which to construct meta types. */ meta_factory(meta_ctx &area) noexcept : ctx{&area}, bucket{}, info{&type_id()} { auto &&elem = internal::owner(*ctx, *info); if(!elem.details) { elem.details = std::make_shared(); } bucket = &elem.details->prop; } /** * @brief Assigns a custom unique identifier to a meta type. * @param id A custom unique identifier. * @return An extended meta factory for the given type. */ auto type(const id_type id) noexcept { auto &&elem = internal::owner(*ctx, *info); ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); bucket = &elem.details->prop; elem.id = id; 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 auto base() noexcept { static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); internal::meta_extend( internal::owner(*ctx, *info), type_id().hash(), internal::meta_base_node{ &internal::resolve, +[](const void *instance) noexcept { return static_cast(static_cast(static_cast(instance))); }}); bucket = nullptr; 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 auto conv() noexcept { using conv_type = std::remove_cv_t>>; internal::meta_extend( internal::owner(*ctx, *info), type_id().hash(), internal::meta_conv_node{ +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); }}); bucket = nullptr; 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 auto conv() noexcept { using conv_type = std::remove_cv_t>; internal::meta_extend( internal::owner(*ctx, *info), type_id().hash(), internal::meta_conv_node{ +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast(*static_cast(instance))); }}); bucket = nullptr; return *this; } /** * @brief Assigns a meta constructor to a meta type. * * Both member functions and free function can be assigned to meta types in * the role of constructors. All that is required is that they return an * instance of the underlying type.
* From a client's point of view, nothing changes if a constructor of a meta * type is a built-in one or not. * * @tparam Candidate The actual function to use as a constructor. * @tparam Policy Optional policy (no policy set by default). * @return An extended meta factory for the parent type. */ template auto ctor() noexcept { using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); internal::meta_extend( internal::owner(*ctx, *info), type_id().hash(), internal::meta_ctor_node{ descriptor::args_type::size, &meta_arg, &meta_construct}); bucket = nullptr; 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. * @return An extended meta factory for the parent type. */ template auto ctor() noexcept { // default constructor is already implicitly generated, no need for redundancy if constexpr(sizeof...(Args) != 0u) { using descriptor = meta_function_helper_t; internal::meta_extend( internal::owner(*ctx, *info), type_id().hash(), internal::meta_ctor_node{ descriptor::args_type::size, &meta_arg, &meta_construct}); } bucket = nullptr; return *this; } /** * @brief Assigns a meta destructor to a meta type. * * Both free functions and member functions can be assigned to meta types in * the role of destructors.
* The signature of a free function should be identical to the following: * * @code{.cpp} * void(Type &); * @endcode * * Member functions should not take arguments instead.
* 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() noexcept { static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); internal::meta_extend( internal::owner(*ctx, *info), internal::meta_dtor_node{ +[](void *instance) { std::invoke(Func, *static_cast(instance)); }}); bucket = nullptr; 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). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto data(const id_type id) noexcept { if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t>; auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ /* this is never static */ std::is_const_v ? internal::meta_traits::is_const : internal::meta_traits::is_none, 1u, &internal::resolve>, &meta_arg>>, &meta_setter, &meta_getter}); bucket = &elem.prop; } else { using data_type = std::remove_reference_t>; auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ ((std::is_same_v> || std::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, 1u, &internal::resolve>, &meta_arg>>, &meta_setter, &meta_getter}); bucket = &elem.prop; } 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). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto data(const id_type id) noexcept { using data_type = std::invoke_result_t; static_assert(Policy::template value, "Invalid return type for the given policy"); if constexpr(std::is_same_v) { auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ /* this is never static */ internal::meta_traits::is_const, 0u, &internal::resolve>>, &meta_arg>, &meta_setter, &meta_getter}); bucket = &elem.prop; } else { using args_type = typename meta_function_helper_t::args_type; auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_data_node{ /* this is never static nor const */ internal::meta_traits::is_none, 1u, &internal::resolve>>, &meta_arg>>, &meta_setter, &meta_getter}); bucket = &elem.prop; } return *this; } /** * @brief Assigns a meta data to a meta type by means of its setters and * getter. * * Multi-setter support for meta data members. All setters are tried in the * order of definition before returning to the caller.
* Setters can be either free functions, member functions or a mix of them * and are provided via a `value_list` type. * * @sa data * * @tparam Setter The actual functions to use as setters. * @tparam Getter The actual getter function. * @tparam Policy Optional policy (no policy set by default). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto data(const id_type id) noexcept { data(id, std::make_index_sequence{}); return *this; } /** * @brief Assigns a meta function to a meta type. * * Both member functions and free functions can be assigned to a meta * type.
* From a client's point of view, all the functions associated with the * reflected object will appear as if they were part of the type itself. * * @tparam Candidate The actual function to attach to the meta type. * @tparam Policy Optional policy (no policy set by default). * @param id Unique identifier. * @return An extended meta factory for the parent type. */ template auto func(const id_type id) noexcept { using descriptor = meta_function_helper_t; static_assert(Policy::template value, "Invalid return type for the given policy"); auto &&elem = internal::meta_extend( internal::owner(*ctx, *info), id, internal::meta_func_node{ (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), descriptor::args_type::size, &internal::resolve, void, std::remove_cv_t>>>, &meta_arg, &meta_invoke}); bucket = &elem.prop; return *this; } /** * @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 Value Optional type of the property value. * @param id Property key. * @param value Optional property value. * @return An extended meta factory for the given type. */ template meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) { ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties"); if constexpr(sizeof...(Value) == 0u) { internal::meta_extend( *bucket, id, internal::meta_prop_node{ &internal::resolve}); } else { internal::meta_extend( *bucket, id, internal::meta_prop_node{ &internal::resolve>..., std::make_shared>(std::forward(value))...}); } return *this; } private: meta_ctx *ctx; dense_map *bucket; const type_info *info; }; /** * @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. * @param ctx The context into which to construct meta types. * @return A meta factory for the given type. */ template [[nodiscard]] auto meta(meta_ctx &ctx) noexcept { auto &&context = internal::meta_context::from(ctx); // make sure the type exists in the context before returning a factory context.value.try_emplace(type_id().hash(), internal::resolve(context)); return meta_factory{ctx}; } /** * @brief Utility function to use for reflection. * * This is the point from which everything starts.
* By invoking this function with a type that is not yet reflected, a meta type * is created to which it will be possible to attach meta objects through a * dedicated factory. * * @tparam Type Type to reflect. * @return A meta factory for the given type. */ template [[nodiscard]] auto meta() noexcept { return meta(locator::value_or()); } /** * @brief Resets a type and all its parts. * * Resets a type and all its data members, member functions and properties, as * well as its constructors, destructors and conversion functions if any.
* Base classes aren't reset but the link between the two types is removed. * * The type is also removed from the set of searchable types. * * @param id Unique identifier. * @param ctx The context from which to reset meta types. */ inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { auto &&context = internal::meta_context::from(ctx); for(auto it = context.value.begin(); it != context.value.end();) { if(it->second.id == id) { it = context.value.erase(it); } else { ++it; } } } /** * @brief Resets a type and all its parts. * * Resets a type and all its data members, member functions and properties, as * well as its constructors, destructors and conversion functions if any.
* Base classes aren't reset but the link between the two types is removed. * * The type is also removed from the set of searchable types. * * @param id Unique identifier. */ inline void meta_reset(const id_type id) noexcept { meta_reset(locator::value_or(), id); } /** * @brief Resets a type and all its parts. * * @sa meta_reset * * @tparam Type Type to reset. * @param ctx The context from which to reset meta types. */ template void meta_reset(meta_ctx &ctx) noexcept { internal::meta_context::from(ctx).value.erase(type_id().hash()); } /** * @brief Resets a type and all its parts. * * @sa meta_reset * * @tparam Type Type to reset. */ template void meta_reset() noexcept { meta_reset(locator::value_or()); } /** * @brief Resets all meta types. * * @sa meta_reset * * @param ctx The context from which to reset meta types. */ inline void meta_reset(meta_ctx &ctx) noexcept { internal::meta_context::from(ctx).value.clear(); } /** * @brief Resets all meta types. * * @sa meta_reset */ inline void meta_reset() noexcept { meta_reset(locator::value_or()); } } // namespace entt #endif // #include "meta/meta.hpp" #ifndef ENTT_META_META_HPP #define ENTT_META_META_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/any.hpp" // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" // #include "../locator/locator.hpp" // #include "adl_pointer.hpp" // #include "context.hpp" // #include "fwd.hpp" // #include "node.hpp" // #include "range.hpp" // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; /*! @brief Proxy object for sequence containers. */ class meta_sequence_container { class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_sequence_container(const meta_ctx &area = locator::value_or()) noexcept : ctx{&area} {} /** * @brief Rebinds a proxy object to a sequence container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template void rebind(any instance) noexcept { value_type_node = &internal::resolve; size_fn = &meta_sequence_container_traits::size; resize_fn = &meta_sequence_container_traits::resize; iter_fn = &meta_sequence_container_traits::iter; insert_or_erase_fn = &meta_sequence_container_traits::insert_or_erase; storage = std::move(instance); } [[nodiscard]] inline meta_type value_type() const noexcept; [[nodiscard]] inline size_type size() const noexcept; inline bool resize(const size_type); inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline iterator insert(iterator, meta_any); inline iterator erase(iterator); [[nodiscard]] inline meta_any operator[](const size_type); [[nodiscard]] inline explicit operator bool() const noexcept; private: const meta_ctx *ctx{}; internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; size_type (*size_fn)(const any &) noexcept {}; bool (*resize_fn)(any &, size_type){}; iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; iterator (*insert_or_erase_fn)(const meta_ctx &, any &, const any &, meta_any &){}; any storage{}; }; /*! @brief Proxy object for associative containers. */ class meta_associative_container { class meta_iterator; public: /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Meta iterator type. */ using iterator = meta_iterator; /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_associative_container(const meta_ctx &area = locator::value_or()) noexcept : ctx{&area} {} /** * @brief Rebinds a proxy object to an associative container type. * @tparam Type Type of container to wrap. * @param instance The container to wrap. */ template void rebind(any instance) noexcept { if constexpr(!meta_associative_container_traits::key_only) { mapped_type_node = &internal::resolve; } key_only_container = meta_associative_container_traits::key_only; key_type_node = &internal::resolve; value_type_node = &internal::resolve; size_fn = &meta_associative_container_traits::size; clear_fn = &meta_associative_container_traits::clear; iter_fn = &meta_associative_container_traits::iter; insert_or_erase_fn = &meta_associative_container_traits::insert_or_erase; find_fn = &meta_associative_container_traits::find; storage = std::move(instance); } [[nodiscard]] inline bool key_only() const noexcept; [[nodiscard]] inline meta_type key_type() const noexcept; [[nodiscard]] inline meta_type mapped_type() const noexcept; [[nodiscard]] inline meta_type value_type() const noexcept; [[nodiscard]] inline size_type size() const noexcept; inline bool clear(); [[nodiscard]] inline iterator begin(); [[nodiscard]] inline iterator end(); inline bool insert(meta_any); inline bool insert(meta_any, meta_any); inline size_type erase(meta_any); [[nodiscard]] inline iterator find(meta_any); [[nodiscard]] inline explicit operator bool() const noexcept; private: const meta_ctx *ctx{}; bool key_only_container{}; internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; size_type (*size_fn)(const any &) noexcept {}; bool (*clear_fn)(any &){}; iterator (*iter_fn)(const meta_ctx &, any &, const bool){}; size_type (*insert_or_erase_fn)(any &, meta_any &, meta_any &){}; iterator (*find_fn)(const meta_ctx &, any &, meta_any &){}; any storage{}; }; /*! @brief Opaque wrapper for values of any type. */ class meta_any { enum class operation : std::uint8_t { deref, seq, assoc }; using vtable_type = void(const operation, const any &, void *); template static void basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const any &value, [[maybe_unused]] void *other) { static_assert(std::is_same_v>, Type>, "Invalid type"); if constexpr(!std::is_void_v) { switch(op) { case operation::deref: if constexpr(is_meta_pointer_like_v) { if constexpr(std::is_function_v::element_type>) { static_cast(other)->emplace(any_cast(value)); } else if constexpr(!std::is_same_v::element_type>, void>) { using in_place_type = decltype(adl_meta_pointer_like::dereference(any_cast(value))); if constexpr(std::is_constructible_v) { if(const auto &pointer_like = any_cast(value); pointer_like) { static_cast(other)->emplace(adl_meta_pointer_like::dereference(pointer_like)); } } else { static_cast(other)->emplace(adl_meta_pointer_like::dereference(any_cast(value))); } } } break; case operation::seq: if constexpr(is_complete_v>) { static_cast(other)->rebind(std::move(const_cast(value))); } break; case operation::assoc: if constexpr(is_complete_v>) { static_cast(other)->rebind(std::move(const_cast(value))); } break; } } } void release() { if(node.dtor.dtor && owner()) { node.dtor.dtor(storage.data()); } } meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept : storage{std::move(ref)}, ctx{&area}, node{storage ? other.node : internal::meta_type_node{}}, vtable{storage ? other.vtable : &basic_vtable} {} public: /*! Default constructor. */ meta_any() noexcept : meta_any{meta_ctx_arg, locator::value_or()} {} /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept : storage{}, ctx{&area}, node{}, vtable{&basic_vtable} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit meta_any(std::in_place_type_t, Args &&...args) : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param area The context from which to search for meta types. * @param args Parameters to use to construct the instance. */ template explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) : storage{std::in_place_type, std::forward(args)...}, ctx{&area}, node{internal::resolve>>(internal::meta_context::from(*ctx))}, vtable{&basic_vtable>>} {} /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, meta_any>>> meta_any(Type &&value) : meta_any{locator::value_or(), std::forward(value)} {} /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param area The context from which to search for meta types. * @param value An instance of an object to use to initialize the wrapper. */ template, meta_any>>> meta_any(const meta_ctx &area, Type &&value) : meta_any{area, std::in_place_type>, std::forward(value)} {} /** * @brief Context aware copy constructor. * @param area The context from which to search for meta types. * @param other The instance to copy from. */ meta_any(const meta_ctx &area, const meta_any &other) : meta_any{other} { ctx = &area; node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; } /** * @brief Context aware move constructor. * @param area The context from which to search for meta types. * @param other The instance to move from. */ meta_any(const meta_ctx &area, meta_any &&other) : meta_any{std::move(other)} { ctx = &area; node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; } /** * @brief Copy constructor. * @param other The instance to copy from. */ meta_any(const meta_any &other) = default; /** * @brief Move constructor. * @param other The instance to move from. */ meta_any(meta_any &&other) noexcept : storage{std::move(other.storage)}, ctx{other.ctx}, node{std::exchange(other.node, internal::meta_type_node{})}, vtable{std::exchange(other.vtable, &basic_vtable)} {} /*! @brief Frees the internal storage, whatever it means. */ ~meta_any() { release(); } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This meta any object. */ meta_any &operator=(const meta_any &other) { release(); storage = other.storage; ctx = other.ctx; node = other.node; vtable = other.vtable; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This meta any object. */ meta_any &operator=(meta_any &&other) noexcept { release(); storage = std::move(other.storage); ctx = other.ctx; node = std::exchange(other.node, internal::meta_type_node{}); vtable = std::exchange(other.vtable, &basic_vtable); return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This meta any object. */ template std::enable_if_t, meta_any>, meta_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /*! @copydoc any::type */ [[nodiscard]] inline meta_type type() const noexcept; /*! @copydoc any::data */ [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc any::data */ [[nodiscard]] void *data() noexcept { return storage.data(); } /** * @brief Invokes the underlying function, if possible. * @tparam Args Types of arguments to use to invoke the function. * @param id Unique identifier. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, Args &&...args) const; /*! @copydoc invoke */ template meta_any invoke(const id_type id, Args &&...args); /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param id Unique identifier. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, Type &&value); /** * @brief Gets the value of a given variable. * @param id Unique identifier. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id) const; /*! @copydoc get */ [[nodiscard]] meta_any get(const id_type id); /** * @brief Tries to cast an instance to a given type. * @tparam Type Type to which to cast the instance. * @return A (possibly null) pointer to the contained instance. */ template [[nodiscard]] const Type *try_cast() const { const auto other = internal::resolve>(internal::meta_context::from(*ctx)); return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); } /*! @copydoc try_cast */ template [[nodiscard]] Type *try_cast() { if constexpr(std::is_const_v) { return std::as_const(*this).try_cast>(); } else { const auto other = internal::resolve>(internal::meta_context::from(*ctx)); return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); } } /** * @brief Tries to cast an instance to a given type. * * @warning * Attempting to perform an invalid cast results is undefined behavior. * * @tparam Type Type to which to cast the instance. * @return A reference to the contained instance. */ template [[nodiscard]] Type cast() const { auto *const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc cast */ template [[nodiscard]] Type cast() { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = try_cast>(); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /** * @brief Converts an object in such a way that a given cast becomes viable. * @param type Meta type to which the cast is requested. * @return A valid meta any object if there exists a viable conversion, an * invalid one otherwise. */ [[nodiscard]] meta_any allow_cast(const meta_type &type) const; /** * @brief Converts an object in such a way that a given cast becomes viable. * @param type Meta type to which the cast is requested. * @return True if there exists a viable conversion, false otherwise. */ [[nodiscard]] bool allow_cast(const meta_type &type) { if(auto other = std::as_const(*this).allow_cast(type); other) { if(other.owner()) { std::swap(*this, other); } return true; } return false; } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return A valid meta any object if there exists a viable conversion, an * invalid one otherwise. */ template [[nodiscard]] meta_any allow_cast() const { if constexpr(std::is_reference_v && !std::is_const_v>) { return meta_any{meta_ctx_arg, *ctx}; } else { auto other = internal::resolve>>(internal::meta_context::from(*ctx)); return allow_cast(meta_type{*ctx, other}); } } /** * @brief Converts an object in such a way that a given cast becomes viable. * @tparam Type Type to which the cast is requested. * @return True if there exists a viable conversion, false otherwise. */ template bool allow_cast() { auto other = internal::resolve>>(internal::meta_context::from(*ctx)); return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); } /*! @copydoc any::emplace */ template void emplace(Args &&...args) { release(); storage.emplace(std::forward(args)...); node = internal::resolve>>(internal::meta_context::from(*ctx)); vtable = &basic_vtable>>; } /*! @copydoc any::assign */ bool assign(const meta_any &other); /*! @copydoc any::assign */ bool assign(meta_any &&other); /*! @copydoc any::reset */ void reset() { release(); storage.reset(); node = {}; vtable = &basic_vtable; } /** * @brief Returns a sequence container proxy. * @return A sequence container proxy for the underlying object. */ [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { any detached = storage.as_ref(); meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /*! @copydoc as_sequence_container */ [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { any detached = storage.as_ref(); meta_sequence_container proxy{*ctx}; vtable(operation::seq, detached, &proxy); return proxy; } /** * @brief Returns an associative container proxy. * @return An associative container proxy for the underlying object. */ [[nodiscard]] meta_associative_container as_associative_container() noexcept { any detached = storage.as_ref(); meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /*! @copydoc as_associative_container */ [[nodiscard]] meta_associative_container as_associative_container() const noexcept { any detached = storage.as_ref(); meta_associative_container proxy{*ctx}; vtable(operation::assoc, detached, &proxy); return proxy; } /** * @brief Indirection operator for dereferencing opaque objects. * @return A wrapper that shares a reference to an unmanaged object if the * wrapped element is dereferenceable, an invalid meta any otherwise. */ [[nodiscard]] meta_any operator*() const noexcept { meta_any ret{meta_ctx_arg, *ctx}; vtable(operation::deref, storage, &ret); return ret; } /** * @brief Returns false if a wrapper is invalid, true otherwise. * @return False if the wrapper is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return !(node.info == nullptr); } /*! @copydoc any::operator== */ [[nodiscard]] bool operator==(const meta_any &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); } /*! @copydoc any::operator!= */ [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { return !(*this == other); } /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() noexcept { return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::as_ref */ [[nodiscard]] meta_any as_ref() const noexcept { return meta_any{*ctx, *this, storage.as_ref()}; } /*! @copydoc any::owner */ [[nodiscard]] bool owner() const noexcept { return storage.owner(); } private: any storage; const meta_ctx *ctx; internal::meta_type_node node; vtable_type *vtable; }; /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @param ctx The context from which to search for meta types. * @return A properly initialized and not necessarily owning wrapper. */ template meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { return meta_any{ctx, std::in_place_type, std::forward(value)}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template meta_any forward_as_meta(Type &&value) { return forward_as_meta(locator::value_or(), std::forward(value)); } /** * @brief Opaque pointers to instances of any type. * * A handle doesn't perform copies and isn't responsible for the contained * object. It doesn't prolong the lifetime of the pointed instance.
* Handles are used to generate references to actual objects when needed. */ struct meta_handle { /*! Default constructor. */ meta_handle() noexcept : meta_handle{meta_ctx_arg, locator::value_or()} {} /** * @brief Context aware constructor. * @param area The context from which to search for meta types. */ meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept : any{meta_ctx_arg, area} {} /** * @brief Creates a handle that points to an unmanaged object. * @param value An instance of an object to use to initialize the handle. */ meta_handle(meta_any &value) noexcept : any{value.as_ref()} {} /** * @brief Creates a handle that points to an unmanaged object. * @param value An instance of an object to use to initialize the handle. */ meta_handle(const meta_any &value) noexcept : any{value.as_ref()} {} /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param ctx The context from which to search for meta types. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(const meta_ctx &ctx, Type &value) noexcept : any{ctx, std::in_place_type, value} {} /** * @brief Creates a handle that points to an unmanaged object. * @tparam Type Type of object to use to initialize the handle. * @param value An instance of an object to use to initialize the handle. */ template, meta_handle>>> meta_handle(Type &value) noexcept : meta_handle{locator::value_or(), value} {} /** * @brief Context aware copy constructor. * @param area The context from which to search for meta types. * @param other The instance to copy from. */ meta_handle(const meta_ctx &area, const meta_handle &other) : any{area, other.any} {} /** * @brief Context aware move constructor. * @param area The context from which to search for meta types. * @param other The instance to move from. */ meta_handle(const meta_ctx &area, meta_handle &&other) : any{area, std::move(other.any)} {} /*! @brief Default copy constructor, deleted on purpose. */ meta_handle(const meta_handle &) = delete; /*! @brief Default move constructor. */ meta_handle(meta_handle &&) = default; /** * @brief Default copy assignment operator, deleted on purpose. * @return This meta handle. */ meta_handle &operator=(const meta_handle &) = delete; /** * @brief Default move assignment operator. * @return This meta handle. */ meta_handle &operator=(meta_handle &&) = default; /** * @brief Returns false if a handle is invalid, true otherwise. * @return False if the handle is invalid, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(any); } /** * @brief Access operator for accessing the contained opaque object. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] meta_any *operator->() { return &any; } /*! @copydoc operator-> */ [[nodiscard]] const meta_any *operator->() const { return &any; } private: meta_any any; }; /*! @brief Opaque wrapper for properties of any type. */ struct meta_prop { /*! @brief Default constructor. */ meta_prop() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the stored value by copy. * @return A wrapper containing the value stored with the property. */ [[nodiscard]] meta_any value() const { return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_prop_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for data members. */ struct meta_data { /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_data_node::size_type; /*! @brief Default constructor. */ meta_data() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the number of setters available. * @return The number of setters available. */ [[nodiscard]] size_type arity() const noexcept { return node->arity; } /** * @brief Indicates whether a data member is constant or not. * @return True if the data member is constant, false otherwise. */ [[nodiscard]] bool is_const() const noexcept { return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a data member is static or not. * @return True if the data member is static, false otherwise. */ [[nodiscard]] bool is_static() const noexcept { return static_cast(node->traits & internal::meta_traits::is_static); } /*! @copydoc meta_any::type */ [[nodiscard]] inline meta_type type() const noexcept; /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(meta_handle instance, Type &&value) const { return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); } /** * @brief Gets the value of a given variable. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(meta_handle instance) const { return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); } /** * @brief Returns the type accepted by the i-th setter. * @param index Index of the setter of which to return the accepted type. * @return The type accepted by the i-th setter. */ [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Returns a range to visit registered meta properties. * @return An iterable range to visit registered meta properties. */ [[nodiscard]] meta_range prop() const noexcept { return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { const auto it = node->prop.find(key); return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_data_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for member functions. */ struct meta_func { /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_func_node::size_type; /*! @brief Default constructor. */ meta_func() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept : node{&curr}, ctx{&area} {} /** * @brief Returns the number of arguments accepted by a member function. * @return The number of arguments accepted by the member function. */ [[nodiscard]] size_type arity() const noexcept { return node->arity; } /** * @brief Indicates whether a member function is constant or not. * @return True if the member function is constant, false otherwise. */ [[nodiscard]] bool is_const() const noexcept { return static_cast(node->traits & internal::meta_traits::is_const); } /** * @brief Indicates whether a member function is static or not. * @return True if the member function is static, false otherwise. */ [[nodiscard]] bool is_static() const noexcept { return static_cast(node->traits & internal::meta_traits::is_static); } /** * @brief Returns the return type of a member function. * @return The return type of the member function. */ [[nodiscard]] inline meta_type ret() const noexcept; /** * @brief Returns the type of the i-th argument of a member function. * @param index Index of the argument of which to return the type. * @return The type of the i-th argument of a member function. */ [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; /** * @brief Invokes the underlying function, if possible. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(meta_handle instance, Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return invoke(meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /*! @copydoc meta_data::prop */ [[nodiscard]] meta_range prop() const noexcept { return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; } /** * @brief Lookup utility for meta properties. * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { const auto it = node->prop.find(key); return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; } /** * @brief Returns the next overload of a given function, if any. * @return The next overload of the given function, if any. */ [[nodiscard]] meta_func next() const { return node->next ? meta_func{*ctx, *node->next} : meta_func{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return (node != nullptr); } private: const internal::meta_func_node *node; const meta_ctx *ctx; }; /*! @brief Opaque wrapper for types. */ class meta_type { template [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { decltype(next()) candidate = nullptr; size_type same{}; bool ambiguous{}; for(auto curr = next(); curr; curr = next()) { if constexpr(std::is_same_v, internal::meta_func_node>) { if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { continue; } } if(curr->arity == sz) { size_type match{}; size_type pos{}; for(; pos < sz && args[pos]; ++pos) { const auto other = curr->arg(*ctx, pos); const auto type = args[pos].type(); if(const auto &info = other.info(); info == type.info()) { ++match; } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { break; } } if(pos == sz) { if(!candidate || match > same) { candidate = curr; same = match; ambiguous = false; } else if(match == same) { if constexpr(std::is_same_v, internal::meta_func_node>) { if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; ambiguous = false; continue; } } ambiguous = true; } } } } return ambiguous ? nullptr : candidate; } public: /*! @brief Unsigned integer type. */ using size_type = typename internal::meta_type_node::size_type; /*! @brief Default constructor. */ meta_type() noexcept : node{}, ctx{} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept : node{curr}, ctx{&area} {} /** * @brief Context aware constructor for meta objects. * @param area The context from which to search for meta types. * @param curr The underlying node with which to construct the instance. */ meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept : meta_type{area, curr.type(internal::meta_context::from(area))} {} /** * @brief Returns the type info object of the underlying type. * @return The type info object of the underlying type. */ [[nodiscard]] const type_info &info() const noexcept { return *node.info; } /** * @brief Returns the identifier assigned to a type. * @return The identifier assigned to the type. */ [[nodiscard]] id_type id() const noexcept { return node.id; } /** * @brief Returns the size of the underlying type if known. * @return The size of the underlying type if known, 0 otherwise. */ [[nodiscard]] size_type size_of() const noexcept { return node.size_of; } /** * @brief Checks whether a type refers to an arithmetic type or not. * @return True if the underlying type is an arithmetic type, false * otherwise. */ [[nodiscard]] bool is_arithmetic() const noexcept { return static_cast(node.traits & internal::meta_traits::is_arithmetic); } /** * @brief Checks whether a type refers to an integral type or not. * @return True if the underlying type is an integral type, false otherwise. */ [[nodiscard]] bool is_integral() const noexcept { return static_cast(node.traits & internal::meta_traits::is_integral); } /** * @brief Checks whether a type refers to a signed type or not. * @return True if the underlying type is a signed type, false otherwise. */ [[nodiscard]] bool is_signed() const noexcept { return static_cast(node.traits & internal::meta_traits::is_signed); } /** * @brief Checks whether a type refers to an array type or not. * @return True if the underlying type is an array type, false otherwise. */ [[nodiscard]] bool is_array() const noexcept { return static_cast(node.traits & internal::meta_traits::is_array); } /** * @brief Checks whether a type refers to an enum or not. * @return True if the underlying type is an enum, false otherwise. */ [[nodiscard]] bool is_enum() const noexcept { return static_cast(node.traits & internal::meta_traits::is_enum); } /** * @brief Checks whether a type refers to a class or not. * @return True if the underlying type is a class, false otherwise. */ [[nodiscard]] bool is_class() const noexcept { return static_cast(node.traits & internal::meta_traits::is_class); } /** * @brief Checks whether a type refers to a pointer or not. * @return True if the underlying type is a pointer, false otherwise. */ [[nodiscard]] bool is_pointer() const noexcept { return node.info && (node.info->hash() != remove_pointer().info().hash()); } /** * @brief Provides the type for which the pointer is defined. * @return The type for which the pointer is defined or this type if it * doesn't refer to a pointer type. */ [[nodiscard]] meta_type remove_pointer() const noexcept { return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; } /** * @brief Checks whether a type is a pointer-like type or not. * @return True if the underlying type is a pointer-like one, false * otherwise. */ [[nodiscard]] bool is_pointer_like() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); } /** * @brief Checks whether a type refers to a sequence container or not. * @return True if the type is a sequence container, false otherwise. */ [[nodiscard]] bool is_sequence_container() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); } /** * @brief Checks whether a type refers to an associative container or not. * @return True if the type is an associative container, false otherwise. */ [[nodiscard]] bool is_associative_container() const noexcept { return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); } /** * @brief Checks whether a type refers to a recognized class template * specialization or not. * @return True if the type is a recognized class template specialization, * false otherwise. */ [[nodiscard]] bool is_template_specialization() const noexcept { return (node.templ.arity != 0u); } /** * @brief Returns the number of template arguments. * @return The number of template arguments. */ [[nodiscard]] size_type template_arity() const noexcept { return node.templ.arity; } /** * @brief Returns a tag for the class template of the underlying type. * @return The tag for the class template of the underlying type. */ [[nodiscard]] inline meta_type template_type() const noexcept { return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the type of the i-th template argument of a type. * @param index Index of the template argument of which to return the type. * @return The type of the i-th template argument of a type. */ [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; } /** * @brief Returns a range to visit registered top-level base meta types. * @return An iterable range to visit registered top-level base meta types. */ [[nodiscard]] meta_range base() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; } /** * @brief Returns a range to visit registered top-level meta data. * @return An iterable range to visit registered top-level meta data. */ [[nodiscard]] meta_range data() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; } /** * @brief Lookup utility for meta data (bases are also visited). * @param id Unique identifier. * @return The registered meta data for the given identifier, if any. */ [[nodiscard]] meta_data data(const id_type id) const { if(node.details) { if(const auto it = node.details->data.find(id); it != node.details->data.cend()) { return meta_data{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.data(id); elem) { return elem; } } return meta_data{}; } /** * @brief Returns a range to visit registered top-level functions. * @return An iterable range to visit registered top-level functions. */ [[nodiscard]] meta_range func() const noexcept { using return_type = meta_range; return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; } /** * @brief Lookup utility for meta functions (bases are also visited). * * In case of overloaded functions, the first one with the required * identifier is returned. * * @param id Unique identifier. * @return The registered meta function for the given identifier, if any. */ [[nodiscard]] meta_func func(const id_type id) const { if(node.details) { if(const auto it = node.details->func.find(id); it != node.details->func.cend()) { return meta_func{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.func(id); elem) { return elem; } } return meta_func{}; } /** * @brief Creates an instance of the underlying type, if possible. * * If suitable, the implicitly generated default constructor is used. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param args Parameters to use to construct the instance. * @param sz Number of parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { if(node.details) { if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { return candidate->invoke(*ctx, args); } } if(sz == 0u && node.default_constructor) { return node.default_constructor(*ctx); } return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief construct * @tparam Args Types of arguments to use to construct the instance. * @param args Parameters to use to construct the instance. * @return A wrapper containing the new instance, if any. */ template [[nodiscard]] meta_any construct(Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return construct(arguments, sizeof...(Args)); } /** * @brief Wraps an opaque element of the underlying type. * @param element A valid pointer to an element of the underlying type. * @return A wrapper that references the given instance. */ meta_any from_void(void *element) const { return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; } /*! @copydoc from_void */ meta_any from_void(const void *element) const { return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Invokes a function given an identifier, if possible. * * @warning * The context of the arguments is **not** changed.
* It's up to the caller to bind them to the right context(s). * * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @param sz Number of parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { if(node.details) { if(auto it = node.details->func.find(id); it != node.details->func.cend()) { if(const auto *candidate = lookup(args, sz, (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); } } } for(auto &&curr: base()) { if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { return elem; } } return meta_any{meta_ctx_arg, *ctx}; } /** * @copybrief invoke * * @param id Unique identifier. * @tparam Args Types of arguments to use to invoke the function. * @param instance An opaque instance of the underlying type. * @param args Parameters to use to invoke the function. * @return A wrapper containing the returned value, if any. */ template meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { meta_any arguments[sizeof...(Args) + 1u]{{*ctx, std::forward(args)}...}; return invoke(id, meta_handle{*ctx, std::move(instance)}, arguments, sizeof...(Args)); } /** * @brief Sets the value of a given variable. * @tparam Type Type of value to assign. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @param value Parameter to use to set the underlying variable. * @return True in case of success, false otherwise. */ template bool set(const id_type id, meta_handle instance, Type &&value) const { const auto candidate = data(id); return candidate && candidate.set(std::move(instance), std::forward(value)); } /** * @brief Gets the value of a given variable. * @param id Unique identifier. * @param instance An opaque instance of the underlying type. * @return A wrapper containing the value of the underlying variable. */ [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { const auto candidate = data(id); return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; } /** * @brief Returns a range to visit registered top-level meta properties. * @return An iterable range to visit registered top-level meta properties. */ [[nodiscard]] meta_range prop() const noexcept { using range_type = meta_range; return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; } /** * @brief Lookup utility for meta properties (bases are also visited). * @param key The key to use to search for a property. * @return The registered meta property for the given key, if any. */ [[nodiscard]] meta_prop prop(const id_type key) const { if(node.details) { if(const auto it = node.details->prop.find(key); it != node.details->prop.cend()) { return meta_prop{*ctx, it->second}; } } for(auto &&curr: base()) { if(auto elem = curr.second.prop(key); elem) { return elem; } } return meta_prop{}; } /** * @brief Returns true if an object is valid, false otherwise. * @return True if the object is valid, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return !(ctx == nullptr); } /** * @brief Checks if two objects refer to the same type. * @param other The object with which to compare. * @return True if the objects refer to the same type, false otherwise. */ [[nodiscard]] bool operator==(const meta_type &other) const noexcept { return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); } private: internal::meta_type_node node; const meta_ctx *ctx; }; /** * @brief Checks if two objects refer to the same type. * @param lhs An object, either valid or not. * @param rhs An object, either valid or not. * @return False if the objects refer to the same node, true otherwise. */ [[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { return !(lhs == rhs); } [[nodiscard]] inline meta_type meta_any::type() const noexcept { return node.info ? meta_type{*ctx, node} : meta_type{}; } template meta_any meta_any::invoke(const id_type id, Args &&...args) const { return type().invoke(id, *this, std::forward(args)...); } template meta_any meta_any::invoke(const id_type id, Args &&...args) { return type().invoke(id, *this, std::forward(args)...); } template bool meta_any::set(const id_type id, Type &&value) { return type().set(id, *this, std::forward(value)); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) const { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::get(const id_type id) { return type().get(id, *this); } [[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { if(node.info && *node.info == type.info()) { return as_ref(); } if(const auto *value = data(); node.details) { if(auto it = node.details->conv.find(type.info().hash()); it != node.details->conv.cend()) { return it->second.conv(*ctx, data()); } for(auto &&curr: node.details->base) { const auto &as_const = curr.second.type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, curr.second.cast(value)); if(auto other = as_const.allow_cast(type); other) { return other; } } } if(node.conversion_helper && (type.is_arithmetic() || type.is_enum())) { // exploits the fact that arithmetic types and enums are also default constructible auto other = type.construct(); ENTT_ASSERT(other.node.conversion_helper, "Conversion helper not found"); const auto value = node.conversion_helper(nullptr, storage.data()); other.node.conversion_helper(other.storage.data(), &value); return other; } return meta_any{meta_ctx_arg, *ctx}; } inline bool meta_any::assign(const meta_any &other) { auto value = other.allow_cast({*ctx, node}); return value && storage.assign(std::move(value.storage)); } inline bool meta_any::assign(meta_any &&other) { if(*node.info == *other.node.info) { return storage.assign(std::move(other.storage)); } return assign(std::as_const(other)); } [[nodiscard]] inline meta_type meta_data::type() const noexcept { return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; } [[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { return index < arity() ? node->arg(*ctx, index) : meta_type{}; } [[nodiscard]] inline meta_type meta_func::ret() const noexcept { return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; } [[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { return index < arity() ? node->arg(*ctx, index) : meta_type{}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ class meta_sequence_container::meta_iterator final { friend class meta_sequence_container; enum class operation : std::uint8_t { incr, deref }; using vtable_type = void(const operation, const any &, const std::ptrdiff_t, meta_any *); template static void basic_vtable(const operation op, const any &value, const std::ptrdiff_t offset, meta_any *other) { switch(op) { case operation::incr: { auto &it = any_cast(const_cast(value)); it = std::next(it, offset); } break; case operation::deref: { const auto &it = any_cast(value); other->emplace(*it); } break; } } public: using difference_type = std::ptrdiff_t; using value_type = meta_any; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr meta_iterator() noexcept : ctx{}, vtable{}, handle{} {} template explicit meta_iterator(const meta_ctx &area, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, handle{std::move(iter)} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, 1, nullptr); return *this; } meta_iterator operator++(int value) noexcept { meta_iterator orig = *this; vtable(operation::incr, handle, ++value, nullptr); return orig; } meta_iterator &operator--() noexcept { vtable(operation::incr, handle, -1, nullptr); return *this; } meta_iterator operator--(int value) noexcept { meta_iterator orig = *this; vtable(operation::incr, handle, --value, nullptr); return orig; } [[nodiscard]] reference operator*() const { reference other{meta_ctx_arg, *ctx}; vtable(operation::deref, handle, 0, &other); return other; } [[nodiscard]] pointer operator->() const { return operator*(); } [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: const meta_ctx *ctx; vtable_type *vtable; any handle; }; class meta_associative_container::meta_iterator final { enum class operation : std::uint8_t { incr, deref }; using vtable_type = void(const operation, const any &, std::pair *); template static void basic_vtable(const operation op, const any &value, std::pair *other) { switch(op) { case operation::incr: ++any_cast(const_cast(value)); break; case operation::deref: const auto &it = any_cast(value); if constexpr(KeyOnly) { other->first.emplace(*it); } else { other->first.emplacefirst))>(it->first); other->second.emplacesecond))>(it->second); } break; } } public: using difference_type = std::ptrdiff_t; using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; constexpr meta_iterator() noexcept : ctx{}, vtable{}, handle{} {} template meta_iterator(const meta_ctx &area, std::integral_constant, It iter) noexcept : ctx{&area}, vtable{&basic_vtable}, handle{std::move(iter)} {} meta_iterator &operator++() noexcept { vtable(operation::incr, handle, nullptr); return *this; } meta_iterator operator++(int) noexcept { meta_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] reference operator*() const { reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; vtable(operation::deref, handle, &other); return other; } [[nodiscard]] pointer operator->() const { return operator*(); } [[nodiscard]] explicit operator bool() const noexcept { return static_cast(handle); } [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { return handle == other.handle; } [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { return !(*this == other); } private: const meta_ctx *ctx; vtable_type *vtable; any handle; }; /** * Internal details not to be documented. * @endcond */ /** * @brief Returns the meta value type of a container. * @return The meta value type of the container. */ [[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the size of a container. * @return The size of the container. */ [[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { return size_fn(storage); } /** * @brief Resizes a container to contain a given number of elements. * @param sz The new size of the container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::resize(const size_type sz) { return resize_fn(storage, sz); } /** * @brief Clears the content of a container. * @return True in case of success, false otherwise. */ inline bool meta_sequence_container::clear() { return resize_fn(storage, 0u); } /** * @brief Returns an iterator to the first element of a container. * @return An iterator to the first element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { return iter_fn(*ctx, storage, false); } /** * @brief Returns an iterator that is past the last element of a container. * @return An iterator that is past the last element of the container. */ [[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { return iter_fn(*ctx, storage, true); } /** * @brief Inserts an element at a specified location of a container. * @param it Iterator before which the element will be inserted. * @param value Element value to insert. * @return A possibly invalid iterator to the inserted element. */ inline meta_sequence_container::iterator meta_sequence_container::insert(iterator it, meta_any value) { return insert_or_erase_fn(*ctx, storage, it.handle, value); } /** * @brief Removes a given element from a container. * @param it Iterator to the element to remove. * @return A possibly invalid iterator following the last removed element. */ inline meta_sequence_container::iterator meta_sequence_container::erase(iterator it) { return insert(std::move(it), {}); } /** * @brief Returns a reference to the element at a given location of a container * (no bounds checking is performed). * @param pos The position of the element to return. * @return A reference to the requested element properly wrapped. */ [[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { auto it = begin(); it.operator++(static_cast(pos) - 1); return *it; } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { return static_cast(storage); } /** * @brief Returns true if a container is also key-only, false otherwise. * @return True if the associative container is also key-only, false otherwise. */ [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { return key_only_container; } /** * @brief Returns the meta key type of a container. * @return The meta key type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /** * @brief Returns the meta mapped type of a container. * @return The meta mapped type of the a container. */ [[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::value_type */ [[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; } /*! @copydoc meta_sequence_container::size */ [[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { return size_fn(storage); } /*! @copydoc meta_sequence_container::clear */ inline bool meta_associative_container::clear() { return clear_fn(storage); } /*! @copydoc meta_sequence_container::begin */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { return iter_fn(*ctx, storage, false); } /*! @copydoc meta_sequence_container::end */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { return iter_fn(*ctx, storage, true); } /** * @brief Inserts a key only element into a container. * @param key The key of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key) { meta_any value{*ctx, std::in_place_type}; return (insert_or_erase_fn(storage, key, value) != 0u); } /** * @brief Inserts a key/value element into a container. * @param key The key of the element to insert. * @param value The value of the element to insert. * @return A bool denoting whether the insertion took place. */ inline bool meta_associative_container::insert(meta_any key, meta_any value) { return (insert_or_erase_fn(storage, key, value) != 0u); } /** * @brief Removes the specified element from a container. * @param key The key of the element to remove. * @return A bool denoting whether the removal took place. */ inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { return insert(std::move(key), meta_any{meta_ctx_arg, *ctx}); } /** * @brief Returns an iterator to the element with a given key, if any. * @param key The key of the element to search. * @return An iterator to the element with the given key, if any. */ [[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { return find_fn(*ctx, storage, key); } /** * @brief Returns false if a proxy is invalid, true otherwise. * @return False if the proxy is invalid, true otherwise. */ [[nodiscard]] inline meta_associative_container::operator bool() const noexcept { return static_cast(storage); } } // namespace entt #endif // #include "meta/node.hpp" #ifndef ENTT_META_NODE_HPP #define ENTT_META_NODE_HPP #include #include #include #include // #include "../config/config.h" // #include "../container/dense_map.hpp" // #include "../core/attribute.h" // #include "../core/enum.hpp" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/type_traits.hpp" // #include "../core/utility.hpp" // #include "context.hpp" // #include "type_traits.hpp" namespace entt { class meta_any; class meta_type; struct meta_handle; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class meta_traits : std::uint32_t { is_none = 0x0000, is_const = 0x0001, is_static = 0x0002, is_arithmetic = 0x0004, is_integral = 0x0008, is_signed = 0x0010, is_array = 0x0020, is_enum = 0x0040, is_class = 0x0080, is_meta_pointer_like = 0x0100, is_meta_sequence_container = 0x0200, is_meta_associative_container = 0x0400, _entt_enum_as_bitmask }; struct meta_type_node; struct meta_prop_node { meta_type_node (*type)(const meta_context &) noexcept {}; std::shared_ptr value{}; }; struct meta_base_node { meta_type_node (*type)(const meta_context &) noexcept {}; const void *(*cast)(const void *) noexcept {}; }; struct meta_conv_node { meta_any (*conv)(const meta_ctx &, const void *){}; }; struct meta_ctor_node { using size_type = std::size_t; size_type arity{0u}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; meta_any (*invoke)(const meta_ctx &, meta_any *const){}; }; struct meta_dtor_node { void (*dtor)(void *){}; }; struct meta_data_node { using size_type = std::size_t; meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node (*type)(const meta_context &) noexcept {}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; bool (*set)(meta_handle, meta_any){}; meta_any (*get)(const meta_ctx &, meta_handle){}; dense_map prop{}; }; struct meta_func_node { using size_type = std::size_t; meta_traits traits{meta_traits::is_none}; size_type arity{0u}; meta_type_node (*ret)(const meta_context &) noexcept {}; meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; std::shared_ptr next{}; dense_map prop{}; }; struct meta_template_node { using size_type = std::size_t; size_type arity{0u}; meta_type_node (*type)(const meta_context &) noexcept {}; meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; }; struct meta_type_descriptor { dense_map ctor{}; dense_map base{}; dense_map conv{}; dense_map data{}; dense_map func{}; dense_map prop{}; }; struct meta_type_node { using size_type = std::size_t; const type_info *info{}; id_type id{}; meta_traits traits{meta_traits::is_none}; size_type size_of{0u}; meta_type_node (*resolve)(const meta_context &) noexcept {}; meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; meta_any (*default_constructor)(const meta_ctx &){}; double (*conversion_helper)(void *, const void *){}; meta_any (*from_void)(const meta_ctx &, void *, const void *){}; meta_template_node templ{}; meta_dtor_node dtor{}; std::shared_ptr details{}; }; template meta_type_node resolve(const meta_context &) noexcept; template [[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { [[maybe_unused]] std::size_t pos{}; meta_type_node (*value)(const meta_context &) noexcept = nullptr; ((value = (pos++ == index ? &resolve>> : value)), ...); ENTT_ASSERT(value != nullptr, "Out of bounds"); return value(context); } [[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { if(from.info && to.info && *from.info == *to.info) { return instance; } if(from.details) { for(auto &&curr: from.details->base) { if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { return elem; } } } return nullptr; } [[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { const auto it = context.value.find(info.hash()); return it != context.value.end() ? &it->second : nullptr; } template [[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { static_assert(std::is_same_v>>, "Invalid type"); if(auto *elem = try_resolve(context, type_id()); elem) { return *elem; } meta_type_node node{ &type_id(), type_id().hash(), (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), size_of_v, &resolve, &resolve>>}; if constexpr(std::is_default_constructible_v) { node.default_constructor = +[](const meta_ctx &ctx) { return meta_any{ctx, std::in_place_type}; }; } if constexpr(std::is_arithmetic_v) { node.conversion_helper = +[](void *bin, const void *value) { return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); }; } else if constexpr(std::is_enum_v) { node.conversion_helper = +[](void *bin, const void *value) { return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); }; } if constexpr(!std::is_same_v && !std::is_function_v) { node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { if(element) { return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; } return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; }; } if constexpr(is_complete_v>) { node.templ = meta_template_node{ meta_template_traits::args_type::size, &resolve::class_type>, +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; } return node; } } // namespace internal /** * Internal details not to be documented. * @endcond */ } // namespace entt #endif // #include "meta/pointer.hpp" #ifndef ENTT_META_POINTER_HPP #define ENTT_META_POINTER_HPP #include #include // #include "type_traits.hpp" namespace entt { /** * @brief Makes plain pointers pointer-like types for the meta system. * @tparam Type Element type. */ template struct is_meta_pointer_like : std::true_type {}; /** * @brief Partial specialization used to reject pointers to arrays. * @tparam Type Type of elements of the array. * @tparam N Number of elements of the array. */ template struct is_meta_pointer_like : std::false_type {}; /** * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta * system. * @tparam Type Element type. */ template struct is_meta_pointer_like> : std::true_type {}; /** * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta * system. * @tparam Type Element type. * @tparam Args Other arguments. */ template struct is_meta_pointer_like> : std::true_type {}; } // namespace entt #endif // #include "meta/policy.hpp" #ifndef ENTT_META_POLICY_HPP #define ENTT_META_POLICY_HPP #include namespace entt { /*! @brief Empty class type used to request the _as ref_ policy. */ struct as_ref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = std::is_reference_v && !std::is_const_v>; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as cref_ policy. */ struct as_cref_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = std::is_reference_v; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as-is_ policy. */ struct as_is_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = true; /** * Internal details not to be documented. * @endcond */ }; /*! @brief Empty class type used to request the _as void_ policy. */ struct as_void_t final { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ template static constexpr bool value = true; /** * Internal details not to be documented. * @endcond */ }; /** * @brief Provides the member constant `value` to true if a type also is a meta * policy, false otherwise. * @tparam Type Type to check. */ template struct is_meta_policy : std::disjunction< std::is_same, std::is_same, std::is_same, std::is_same> {}; /** * @brief Helper variable template. * @tparam Type Type to check. */ template inline constexpr bool is_meta_policy_v = is_meta_policy::value; } // namespace entt #endif // #include "meta/range.hpp" #ifndef ENTT_META_RANGE_HPP #define ENTT_META_RANGE_HPP #include #include #include // #include "../core/fwd.hpp" // #include "../core/iterator.hpp" // #include "context.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct meta_range_iterator final { using difference_type = std::ptrdiff_t; using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using iterator_category = std::input_iterator_tag; meta_range_iterator() noexcept : it{}, ctx{} {} meta_range_iterator(const meta_ctx &area, const It iter) noexcept : it{iter}, ctx{&area} {} meta_range_iterator &operator++() noexcept { return ++it, *this; } meta_range_iterator operator++(int) noexcept { meta_range_iterator orig = *this; return ++(*this), orig; } constexpr meta_range_iterator &operator--() noexcept { return --it, *this; } constexpr meta_range_iterator operator--(int) noexcept { meta_range_iterator orig = *this; return operator--(), orig; } constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr meta_range_iterator operator+(const difference_type value) const noexcept { meta_range_iterator copy = *this; return (copy += value); } constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr meta_range_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].first, Type{*ctx, it[value].second}}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->first, Type{*ctx, it->second}}; } template friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; template friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; template friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; private: It it; const meta_ctx *ctx; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { return !(lhs < rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Iterable range to use to iterate all types of meta objects. * @tparam Type Type of meta objects returned. * @tparam It Type of forward iterator. */ template using meta_range = iterable_adaptor>; } // namespace entt #endif // #include "meta/resolve.hpp" #ifndef ENTT_META_RESOLVE_HPP #define ENTT_META_RESOLVE_HPP #include // #include "../core/type_info.hpp" // #include "../locator/locator.hpp" // #include "context.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "range.hpp" namespace entt { /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. * @param ctx The context from which to search for meta types. * @return The meta type associated with the given type, if any. */ template [[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { auto &&context = internal::meta_context::from(ctx); return {ctx, internal::resolve>>(context)}; } /** * @brief Returns the meta type associated with a given type. * @tparam Type Type to use to search for a meta type. * @return The meta type associated with the given type, if any. */ template [[nodiscard]] meta_type resolve() noexcept { return resolve(locator::value_or()); } /** * @brief Returns a range to use to visit all meta types. * @param ctx The context from which to search for meta types. * @return An iterable range to use to visit all meta types. */ [[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { auto &&context = internal::meta_context::from(ctx); return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; } /** * @brief Returns a range to use to visit all meta types. * @return An iterable range to use to visit all meta types. */ [[nodiscard]] inline meta_range resolve() noexcept { return resolve(locator::value_or()); } /** * @brief Returns the meta type associated with a given identifier, if any. * @param ctx The context from which to search for meta types. * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ [[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { for(auto &&curr: resolve(ctx)) { if(curr.second.id() == id) { return curr.second; } } return meta_type{}; } /** * @brief Returns the meta type associated with a given identifier, if any. * @param id Unique identifier. * @return The meta type associated with the given identifier, if any. */ [[nodiscard]] inline meta_type resolve(const id_type id) noexcept { return resolve(locator::value_or(), id); } /** * @brief Returns the meta type associated with a given type info object. * @param ctx The context from which to search for meta types. * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ [[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { auto &&context = internal::meta_context::from(ctx); const auto *elem = internal::try_resolve(context, info); return elem ? meta_type{ctx, *elem} : meta_type{}; } /** * @brief Returns the meta type associated with a given type info object. * @param info The type info object of the requested type. * @return The meta type associated with the given type info object, if any. */ [[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { return resolve(locator::value_or(), info); } } // namespace entt #endif // #include "meta/template.hpp" #ifndef ENTT_META_TEMPLATE_HPP #define ENTT_META_TEMPLATE_HPP // #include "../core/type_traits.hpp" namespace entt { /*! @brief Utility class to disambiguate class templates. */ template class> struct meta_class_template_tag {}; /** * @brief General purpose traits class for generating meta template information. * @tparam Clazz Type of class template. * @tparam Args Types of template arguments. */ template class Clazz, typename... Args> struct meta_template_traits> { /*! @brief Wrapped class template. */ using class_type = meta_class_template_tag; /*! @brief List of template arguments. */ using args_type = type_list; }; } // namespace entt #endif // #include "meta/type_traits.hpp" #ifndef ENTT_META_TYPE_TRAITS_HPP #define ENTT_META_TYPE_TRAITS_HPP #include #include namespace entt { /** * @brief Traits class template to be specialized to enable support for meta * template information. */ template struct meta_template_traits; /** * @brief Traits class template to be specialized to enable support for meta * sequence containers. */ template struct meta_sequence_container_traits; /** * @brief Traits class template to be specialized to enable support for meta * associative containers. */ template struct meta_associative_container_traits; /** * @brief Provides the member constant `value` to true if a given type is a * pointer-like type from the point of view of the meta system, false otherwise. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: std::false_type {}; /** * @brief Partial specialization to ensure that const pointer-like types are * also accepted. * @tparam Type Potentially pointer-like type. */ template struct is_meta_pointer_like: is_meta_pointer_like {}; /** * @brief Helper variable template. * @tparam Type Potentially pointer-like type. */ template inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; } // namespace entt #endif // #include "meta/utility.hpp" #ifndef ENTT_META_UTILITY_HPP #define ENTT_META_UTILITY_HPP #include #include #include #include // #include "../core/type_traits.hpp" // #include "../locator/locator.hpp" // #include "meta.hpp" // #include "node.hpp" // #include "policy.hpp" namespace entt { /** * @brief Meta function descriptor traits. * @tparam Ret Function return type. * @tparam Args Function arguments. * @tparam Static Function staticness. * @tparam Const Function constness. */ template struct meta_function_descriptor_traits { /*! @brief Meta function return type. */ using return_type = Ret; /*! @brief Meta function arguments. */ using args_type = Args; /*! @brief True if the meta function is static, false otherwise. */ static constexpr bool is_static = Static; /*! @brief True if the meta function is const, false otherwise. */ static constexpr bool is_const = Const; }; /*! @brief Primary template isn't defined on purpose. */ template struct meta_function_descriptor; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, true> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam Class Actual owner of the member function. * @tparam Args Function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t, type_list, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta data is associated. * @tparam Class Actual owner of the data member. * @tparam Ret Data member type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret &, std::conditional_t, type_list<>, type_list>, !std::is_base_of_v, false> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. * @tparam MaybeType First function argument. * @tparam Args Other function arguments. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, std::conditional_t>, Type>, type_list, type_list>, !std::is_base_of_v>, Type>, std::is_base_of_v>, Type> && std::is_const_v>> {}; /** * @brief Meta function descriptor. * @tparam Type Reflected type to which the meta function is associated. * @tparam Ret Function return type. */ template struct meta_function_descriptor : meta_function_descriptor_traits< Ret, type_list<>, true, false> {}; /** * @brief Meta function helper. * * Converts a function type to be associated with a reflected type into its meta * function descriptor. * * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template class meta_function_helper { template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); template static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); template static constexpr meta_function_descriptor get_rid_of_noexcept(Class); public: /*! @brief The meta function descriptor of the given function. */ using type = decltype(get_rid_of_noexcept(std::declval())); }; /** * @brief Helper type. * @tparam Type Reflected type to which the meta function is associated. * @tparam Candidate The actual function to associate with the reflected type. */ template using meta_function_helper_t = typename meta_function_helper::type; /** * @brief Wraps a value depending on the given policy. * * This function always returns a wrapped value in the requested context.
* Therefore, if the passed value is itself a wrapped object with a different * context, it undergoes a rebinding to the requested context. * * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param ctx The context from which to search for meta types. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type}; } else if constexpr(std::is_same_v) { return meta_any{ctx, std::in_place_type, value}; } else if constexpr(std::is_same_v) { static_assert(std::is_lvalue_reference_v, "Invalid type"); return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; } else { return meta_any{ctx, std::forward(value)}; } } /** * @brief Wraps a value depending on the given policy. * @tparam Policy Optional policy (no policy set by default). * @tparam Type Type of value to wrap. * @param value Value to wrap. * @return A meta any containing the returned value, if any. */ template std::enable_if_t, meta_any> meta_dispatch(Type &&value) { return meta_dispatch(locator::value_or(), std::forward(value)); } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param ctx The context from which to search for meta types. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { auto &&context = internal::meta_context::from(ctx); return {ctx, internal::meta_arg_node(context, Type{}, index)}; } /** * @brief Returns the meta type of the i-th element of a list of arguments. * @tparam Type Type list of the actual types of arguments. * @param index The index of the element for which to return the meta type. * @return The meta type of the i-th element of the list of arguments. */ template [[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { return meta_arg(locator::value_or(), index); } /** * @brief Sets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to set. * @param instance An opaque instance of the underlying type, if required. * @param value Parameter to use to set the variable. * @return True in case of success, false otherwise. */ template [[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { if constexpr(!std::is_same_v && !std::is_same_v) { if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { using descriptor = meta_function_helper_t; using data_type = type_list_element_t; if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { std::invoke(Data, *clazz, value.cast()); return true; } } else if constexpr(std::is_member_object_pointer_v) { using data_type = std::remove_reference_t::return_type>; if constexpr(!std::is_array_v && !std::is_const_v) { if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { std::invoke(Data, *clazz) = value.cast(); return true; } } } else { using data_type = std::remove_reference_t; if constexpr(!std::is_array_v && !std::is_const_v) { if(value.allow_cast()) { *Data = value.cast(); return true; } } } } return false; } /** * @brief Gets the value of a given variable. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { if constexpr(std::is_member_pointer_v || std::is_function_v>>) { if constexpr(!std::is_array_v>>>) { if constexpr(std::is_invocable_v) { if(auto *clazz = instance->try_cast(); clazz) { return meta_dispatch(ctx, std::invoke(Data, *clazz)); } } if constexpr(std::is_invocable_v) { if(auto *fallback = instance->try_cast(); fallback) { return meta_dispatch(ctx, std::invoke(Data, *fallback)); } } } return meta_any{meta_ctx_arg, ctx}; } else if constexpr(std::is_pointer_v) { if constexpr(std::is_array_v>) { return meta_any{meta_ctx_arg, ctx}; } else { return meta_dispatch(ctx, *Data); } } else { return meta_dispatch(ctx, Data); } } /** * @brief Gets the value of a given variable. * @tparam Type Reflected type to which the variable is associated. * @tparam Data The actual variable to get. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @return A meta any containing the value of the underlying variable. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { return meta_getter(locator::value_or(), std::move(instance)); } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template [[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { if constexpr(std::is_same_v(candidate), args...)), void>) { std::invoke(std::forward(candidate), args...); return meta_any{ctx, std::in_place_type}; } else { return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); } } template [[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { using descriptor = meta_function_helper_t>; if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); } } else { if(((args + Index)->allow_cast>() && ...)) { return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); } } return meta_any{meta_ctx_arg, ctx}; } template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { if(((args + Index)->allow_cast() && ...)) { return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; } return meta_any{meta_ctx_arg, ctx}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Tries to _invoke_ an object given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to _invoke_ an object given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param instance An opaque instance of the underlying type, if required. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); } /** * @brief Tries to invoke a function given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); } /** * @brief Tries to invoke a function given a list of erased parameters. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param instance An opaque instance of the underlying type, if required. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { return meta_invoke(locator::value_or(), std::move(instance), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @param ctx The context from which to search for meta types. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { return internal::meta_construct(ctx, args, std::index_sequence_for{}); } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Actual type of the instance to construct. * @tparam Args Types of arguments expected. * @param args Parameters to use to construct the instance. * @return A meta any containing the new instance, if any. */ template [[nodiscard]] meta_any meta_construct(meta_any *const args) { return meta_construct(locator::value_or(), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param ctx The context from which to search for meta types. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); } else { return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); } } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Reflected type to which the object to _invoke_ is associated. * @tparam Policy Optional policy (no policy set by default). * @tparam Candidate The type of the actual object to _invoke_. * @param candidate The actual object to _invoke_. * @param args Parameters to use to _invoke_ the object. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { return meta_construct(locator::value_or(), std::forward(candidate), args); } /** * @brief Tries to construct an instance given a list of erased parameters. * * @warning * The context provided is used only for the return type.
* It's up to the caller to bind the arguments to the right context(s). * * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param ctx The context from which to search for meta types. * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { return meta_construct(ctx, Candidate, args); } /** * @brief Tries to construct an instance given a list of erased parameters. * @tparam Type Reflected type to which the function is associated. * @tparam Candidate The actual function to invoke. * @tparam Policy Optional policy (no policy set by default). * @param args Parameters to use to invoke the function. * @return A meta any containing the returned value, if any. */ template [[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { return meta_construct(locator::value_or(), args); } } // namespace entt #endif // #include "platform/android-ndk-r17.hpp" #ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP #define ENTT_PLATFORM_ANDROID_NDK_R17_HPP /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ #ifdef __ANDROID__ # include # if __NDK_MAJOR__ == 17 # include # include # include namespace std { namespace internal { template constexpr auto is_invocable(int) -> decltype(std::invoke(std::declval(), std::declval()...), std::true_type{}); template constexpr std::false_type is_invocable(...); template constexpr auto is_invocable_r(int) -> std::enable_if_t(), std::declval()...)), Ret>, std::true_type>; template constexpr std::false_type is_invocable_r(...); } // namespace internal template struct is_invocable: decltype(internal::is_invocable(0)) {}; template inline constexpr bool is_invocable_v = std::is_invocable::value; template struct is_invocable_r: decltype(internal::is_invocable_r(0)) {}; template inline constexpr bool is_invocable_r_v = std::is_invocable_r::value; template struct invoke_result { using type = decltype(std::invoke(std::declval(), std::declval()...)); }; template using invoke_result_t = typename std::invoke_result::type; } // namespace std # endif #endif /** * Internal details not to be documented. * @endcond */ #endif // #include "poly/poly.hpp" #ifndef ENTT_POLY_POLY_HPP #define ENTT_POLY_POLY_HPP #include #include #include #include #include // #include "../core/any.hpp" #ifndef ENTT_CORE_ANY_HPP #define ENTT_CORE_ANY_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "fwd.hpp" // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { enum class any_operation : std::uint8_t { copy, move, transfer, assign, destroy, compare, get }; enum class any_policy : std::uint8_t { owner, ref, cref }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A SBO friendly, type-safe container for single values of any type. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_any { using operation = internal::any_operation; using policy = internal::any_policy; using vtable_type = const void *(const operation, const basic_any &, const void *); struct storage_type { alignas(Align) std::byte data[Len + !Len]; }; template static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len &&std::is_nothrow_move_constructible_v; template static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { static_assert(!std::is_same_v && std::is_same_v>, Type>, "Invalid type"); const Type *element = nullptr; if constexpr(in_situ) { element = value.owner() ? reinterpret_cast(&value.storage) : static_cast(value.instance); } else { element = static_cast(value.instance); } switch(op) { case operation::copy: if constexpr(std::is_copy_constructible_v) { static_cast(const_cast(other))->initialize(*element); } break; case operation::move: if constexpr(in_situ) { if(value.owner()) { return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; } } return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); case operation::transfer: if constexpr(std::is_move_assignable_v) { *const_cast(element) = std::move(*static_cast(const_cast(other))); return other; } [[fallthrough]]; case operation::assign: if constexpr(std::is_copy_assignable_v) { *const_cast(element) = *static_cast(other); return other; } break; case operation::destroy: if constexpr(in_situ) { element->~Type(); } else if constexpr(std::is_array_v) { delete[] element; } else { delete element; } break; case operation::compare: if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { return *element == *static_cast(other) ? other : nullptr; } else { return (element == other) ? other : nullptr; } case operation::get: return element; } return nullptr; } template void initialize([[maybe_unused]] Args &&...args) { info = &type_id>>(); if constexpr(!std::is_void_v) { vtable = basic_vtable>>; if constexpr(std::is_lvalue_reference_v) { static_assert(sizeof...(Args) == 1u && (std::is_lvalue_reference_v && ...), "Invalid arguments"); mode = std::is_const_v> ? policy::cref : policy::ref; instance = (std::addressof(args), ...); } else if constexpr(in_situ>>) { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { new(&storage) std::remove_cv_t>{std::forward(args)...}; } else { new(&storage) std::remove_cv_t>(std::forward(args)...); } } else { if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v>>) { instance = new std::remove_cv_t>{std::forward(args)...}; } else { instance = new std::remove_cv_t>(std::forward(args)...); } } } } basic_any(const basic_any &other, const policy pol) noexcept : instance{other.data()}, info{other.info}, vtable{other.vtable}, mode{pol} {} public: /*! @brief Size of the internal storage. */ static constexpr auto length = Len; /*! @brief Alignment requirement. */ static constexpr auto alignment = Align; /*! @brief Default constructor. */ constexpr basic_any() noexcept : basic_any{std::in_place_type} {} /** * @brief Constructs a wrapper by directly initializing the new object. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_any(std::in_place_type_t, Args &&...args) : instance{}, info{}, vtable{}, mode{policy::owner} { initialize(std::forward(args)...); } /** * @brief Constructs a wrapper from a given value. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. */ template, basic_any>>> basic_any(Type &&value) : basic_any{std::in_place_type>, std::forward(value)} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ basic_any(const basic_any &other) : basic_any{} { if(other.vtable) { other.vtable(operation::copy, other, this); } } /** * @brief Move constructor. * @param other The instance to move from. */ basic_any(basic_any &&other) noexcept : instance{}, info{other.info}, vtable{other.vtable}, mode{other.mode} { if(other.vtable) { other.vtable(operation::move, other, this); } } /*! @brief Frees the internal storage, whatever it means. */ ~basic_any() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } } /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This any object. */ basic_any &operator=(const basic_any &other) { reset(); if(other.vtable) { other.vtable(operation::copy, other, this); } return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This any object. */ basic_any &operator=(basic_any &&other) noexcept { reset(); if(other.vtable) { other.vtable(operation::move, other, this); info = other.info; vtable = other.vtable; mode = other.mode; } return *this; } /** * @brief Value assignment operator. * @tparam Type Type of object to use to initialize the wrapper. * @param value An instance of an object to use to initialize the wrapper. * @return This any object. */ template std::enable_if_t, basic_any>, basic_any &> operator=(Type &&value) { emplace>(std::forward(value)); return *this; } /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] const type_info &type() const noexcept { return *info; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { return vtable ? vtable(operation::get, *this, nullptr) : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data(const type_info &req) const noexcept { return *info == req ? data() : nullptr; } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data() noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data()); } /** * @brief Returns an opaque pointer to the contained instance. * @param req Expected type. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] void *data(const type_info &req) noexcept { return mode == policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&...args) { reset(); initialize(std::forward(args)...); } /** * @brief Assigns a value to the contained object without replacing it. * @param other The value to assign to the contained object. * @return True in case of success, false otherwise. */ bool assign(const basic_any &other) { if(vtable && mode != policy::cref && *info == *other.info) { return (vtable(operation::assign, *this, other.data()) != nullptr); } return false; } /*! @copydoc assign */ bool assign(basic_any &&other) { if(vtable && mode != policy::cref && *info == *other.info) { if(auto *val = other.data(); val) { return (vtable(operation::transfer, *this, val) != nullptr); } else { return (vtable(operation::assign, *this, std::as_const(other).data()) != nullptr); } } return false; } /*! @brief Destroys contained object */ void reset() { if(vtable && owner()) { vtable(operation::destroy, *this, nullptr); } // unnecessary but it helps to detect nasty bugs ENTT_ASSERT((instance = nullptr) == nullptr, ""); info = &type_id(); vtable = nullptr; mode = policy::owner; } /** * @brief Returns false if a wrapper is empty, true otherwise. * @return False if the wrapper is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return vtable != nullptr; } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return False if the two objects differ in their content, true otherwise. */ [[nodiscard]] bool operator==(const basic_any &other) const noexcept { if(vtable && *info == *other.info) { return (vtable(operation::compare, *this, other.data()) != nullptr); } return (!vtable && !other.vtable); } /** * @brief Checks if two wrappers differ in their content. * @param other Wrapper with which to compare. * @return True if the two objects differ in their content, false otherwise. */ [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { return !(*this == other); } /** * @brief Aliasing constructor. * @return A wrapper that shares a reference to an unmanaged object. */ [[nodiscard]] basic_any as_ref() noexcept { return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; } /*! @copydoc as_ref */ [[nodiscard]] basic_any as_ref() const noexcept { return basic_any{*this, policy::cref}; } /** * @brief Returns true if a wrapper owns its object, false otherwise. * @return True if the wrapper owns its object, false otherwise. */ [[nodiscard]] bool owner() const noexcept { return (mode == policy::owner); } private: union { const void *instance; storage_type storage; }; const type_info *info; vtable_type *vtable; policy mode; }; /** * @brief Performs type-safe access to the contained object. * @tparam Type Type to which conversion is required. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. * @param data Target any object. * @return The element converted to the requested type. */ template Type any_cast(const basic_any &data) noexcept { const auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &data) noexcept { // forces const on non-reference types to make them work also with wrappers for const references auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(*instance); } /*! @copydoc any_cast */ template Type any_cast(basic_any &&data) noexcept { if constexpr(std::is_copy_constructible_v>>) { if(auto *const instance = any_cast>(&data); instance) { return static_cast(std::move(*instance)); } else { return any_cast(data); } } else { auto *const instance = any_cast>(&data); ENTT_ASSERT(instance, "Invalid instance"); return static_cast(std::move(*instance)); } } /*! @copydoc any_cast */ template const Type *any_cast(const basic_any *data) noexcept { const auto &info = type_id>(); return static_cast(data->data(info)); } /*! @copydoc any_cast */ template Type *any_cast(basic_any *data) noexcept { if constexpr(std::is_const_v) { // last attempt to make wrappers for const references return their values return any_cast(&std::as_const(*data)); } else { const auto &info = type_id>(); return static_cast(data->data(info)); } } /** * @brief Constructs a wrapper from a given type, passing it all arguments. * @tparam Type Type of object to use to initialize the wrapper. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. * @return A properly initialized wrapper for an object of the given type. */ template::length, std::size_t Align = basic_any::alignment, typename... Args> basic_any make_any(Args &&...args) { return basic_any{std::in_place_type, std::forward(args)...}; } /** * @brief Forwards its argument and avoids copies for lvalue references. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. * @tparam Type Type of argument to use to construct the new instance. * @param value Parameter to use to construct the instance. * @return A properly initialized and not necessarily owning wrapper. */ template::length, std::size_t Align = basic_any::alignment, typename Type> basic_any forward_as_any(Type &&value) { return basic_any{std::in_place_type, std::forward(value)}; } } // namespace entt #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" // #include "fwd.hpp" // #include "hashed_string.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_POLY_FWD_HPP #define ENTT_POLY_FWD_HPP #include namespace entt { template class basic_poly; /** * @brief Alias declaration for the most common use case. * @tparam Concept Concept descriptor. */ template using poly = basic_poly; } // namespace entt #endif namespace entt { /*! @brief Inspector class used to infer the type of the virtual table. */ struct poly_inspector { /** * @brief Generic conversion operator (definition only). * @tparam Type Type to which conversion is requested. */ template operator Type &&() const; /** * @brief Dummy invocation function (definition only). * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param args The arguments to pass to the function. * @return A poly inspector convertible to any type. */ template poly_inspector invoke(Args &&...args) const; /*! @copydoc invoke */ template poly_inspector invoke(Args &&...args); }; /** * @brief Static virtual table factory. * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Alignment requirement. */ template class poly_vtable { using inspector = typename Concept::template type; template static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any &, Args...); template static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any &, Args...); template static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any &, Args...); template static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any &, Args...); template static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any &, Args...); template static auto make_vtable(value_list) noexcept -> decltype(std::make_tuple(vtable_entry(Candidate)...)); template [[nodiscard]] static constexpr auto make_vtable(type_list) noexcept { if constexpr(sizeof...(Func) == 0u) { return decltype(make_vtable(typename Concept::template impl{})){}; } else if constexpr((std::is_function_v && ...)) { return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; } } template static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { if constexpr(std::is_invocable_r_v) { entry = +[](Any &, Args... args) -> Ret { return std::invoke(Candidate, std::forward(args)...); }; } else { entry = +[](Any &instance, Args... args) -> Ret { return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); }; } } template [[nodiscard]] static auto fill_vtable(std::index_sequence) noexcept { vtable_type impl{}; (fill_vtable_entry>>(std::get(impl)), ...); return impl; } using vtable_type = decltype(make_vtable(Concept{})); static constexpr bool is_mono_v = std::tuple_size_v == 1u; public: /*! @brief Virtual table type. */ using type = std::conditional_t, const vtable_type *>; /** * @brief Returns a static virtual table for a specific concept and type. * @tparam Type The type for which to generate the virtual table. * @return A static virtual table for the given concept and type. */ template [[nodiscard]] static type instance() noexcept { static_assert(std::is_same_v>, "Type differs from its decayed form"); static const vtable_type vtable = fill_vtable(std::make_index_sequence::size>{}); if constexpr(is_mono_v) { return std::get<0>(vtable); } else { return &vtable; } } }; /** * @brief Poly base class used to inject functionalities into concepts. * @tparam Poly The outermost poly class. */ template struct poly_base { /** * @brief Invokes a function from the static virtual table. * @tparam Member Index of the function to invoke. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const { const auto &poly = static_cast(self); if constexpr(std::is_function_v>) { return poly.vtable(poly.storage, std::forward(args)...); } else { return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } } /*! @copydoc invoke */ template [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { auto &poly = static_cast(self); if constexpr(std::is_function_v>) { static_assert(Member == 0u, "Unknown member"); return poly.vtable(poly.storage, std::forward(args)...); } else { return std::get(*poly.vtable)(poly.storage, std::forward(args)...); } } }; /** * @brief Shortcut for calling `poly_base::invoke`. * @tparam Member Index of the function to invoke. * @tparam Poly A fully defined poly object. * @tparam Args Types of arguments to pass to the function. * @param self A reference to the poly object that made the call. * @param args The arguments to pass to the function. * @return The return value of the invoked function, if any. */ template decltype(auto) poly_call(Poly &&self, Args &&...args) { return std::forward(self).template invoke(self, std::forward(args)...); } /** * @brief Static polymorphism made simple and within everyone's reach. * * Static polymorphism is a very powerful tool in C++, albeit sometimes * cumbersome to obtain.
* This class aims to make it simple and easy to use. * * @note * Both deduced and defined static virtual tables are supported.
* Moreover, the `poly` class template also works with unmanaged objects. * * @tparam Concept Concept descriptor. * @tparam Len Size of the storage reserved for the small buffer optimization. * @tparam Align Optional alignment requirement. */ template class basic_poly: private Concept::template type>> { /*! @brief A poly base is allowed to snoop into a poly object. */ friend struct poly_base; public: /*! @brief Concept type. */ using concept_type = typename Concept::template type>; /*! @brief Virtual table type. */ using vtable_type = typename poly_vtable::type; /*! @brief Default constructor. */ basic_poly() noexcept : storage{}, vtable{} {} /** * @brief Constructs a poly by directly initializing the new object. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template explicit basic_poly(std::in_place_type_t, Args &&...args) : storage{std::in_place_type, std::forward(args)...}, vtable{poly_vtable::template instance>>()} {} /** * @brief Constructs a poly from a given value. * @tparam Type Type of object to use to initialize the poly. * @param value An instance of an object to use to initialize the poly. */ template>, basic_poly>>> basic_poly(Type &&value) noexcept : basic_poly{std::in_place_type>>, std::forward(value)} {} /** * @brief Returns the object type if any, `type_id()` otherwise. * @return The object type if any, `type_id()` otherwise. */ [[nodiscard]] const type_info &type() const noexcept { return storage.type(); } /** * @brief Returns an opaque pointer to the contained instance. * @return An opaque pointer the contained instance, if any. */ [[nodiscard]] const void *data() const noexcept { return storage.data(); } /*! @copydoc data */ [[nodiscard]] void *data() noexcept { return storage.data(); } /** * @brief Replaces the contained object by creating a new instance directly. * @tparam Type Type of object to use to initialize the poly. * @tparam Args Types of arguments to use to construct the new instance. * @param args Parameters to use to construct the instance. */ template void emplace(Args &&...args) { storage.template emplace(std::forward(args)...); vtable = poly_vtable::template instance>>(); } /*! @brief Destroys contained object */ void reset() { storage.reset(); vtable = {}; } /** * @brief Returns false if a poly is empty, true otherwise. * @return False if the poly is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(storage); } /** * @brief Returns a pointer to the underlying concept. * @return A pointer to the underlying concept. */ [[nodiscard]] concept_type *operator->() noexcept { return this; } /*! @copydoc operator-> */ [[nodiscard]] const concept_type *operator->() const noexcept { return this; } /** * @brief Aliasing constructor. * @return A poly that shares a reference to an unmanaged object. */ [[nodiscard]] basic_poly as_ref() noexcept { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; return ref; } /*! @copydoc as_ref */ [[nodiscard]] basic_poly as_ref() const noexcept { basic_poly ref{}; ref.storage = storage.as_ref(); ref.vtable = vtable; return ref; } private: basic_any storage; vtable_type vtable; }; } // namespace entt #endif // #include "process/process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP #include #include #include 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 : std::uint8_t { uninitialized = 0, running, paused, succeeded, failed, aborted, finished, rejected }; template auto next(std::integral_constant) -> decltype(std::declval().init(), void()) { static_cast(this)->init(); } template auto next(std::integral_constant, Delta delta, void *data) -> decltype(std::declval().update(delta, data), void()) { static_cast(this)->update(delta, data); } template auto next(std::integral_constant) -> decltype(std::declval().succeeded(), void()) { static_cast(this)->succeeded(); } template auto next(std::integral_constant) -> decltype(std::declval().failed(), void()) { static_cast(this)->failed(); } template auto next(std::integral_constant) -> decltype(std::declval().aborted(), void()) { static_cast(this)->aborted(); } void next(...) const 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() 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() 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() 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() 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() noexcept { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } /** * @brief Aborts a process if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. * * @param immediately Requests an immediate operation. */ void abort(const bool immediately = false) { if(alive()) { current = state::aborted; if(immediately) { tick({}); } } } /** * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ [[nodiscard]] bool alive() const noexcept { return current == state::running || current == state::paused; } /** * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ [[nodiscard]] bool finished() const noexcept { return current == state::finished; } /** * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ [[nodiscard]] bool paused() const noexcept { return current == state::paused; } /** * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ [[nodiscard]] bool rejected() const noexcept { return current == state::rejected; } /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void tick(const Delta delta, void *data = nullptr) { switch(current) { case state::uninitialized: next(std::integral_constant{}); current = state::running; break; case state::running: next(std::integral_constant{}, delta, data); break; default: // suppress warnings break; } // if it's dead, it must be notified and removed immediately switch(current) { case state::succeeded: next(std::integral_constant{}); current = state::finished; break; case state::failed: next(std::integral_constant{}); current = state::rejected; break; case state::aborted: next(std::integral_constant{}); current = state::rejected; break; default: // suppress warnings break; } } private: state current{state::uninitialized}; }; /** * @brief Adaptor for lambdas and functors to turn them into processes. * * Lambdas and functors can't be used directly with a scheduler for they are not * properly defined processes with managed life cycles.
* This class helps in filling the gap and turning lambdas and functors into * full featured processes usable by a scheduler. * * The signature of the function call operator should be equivalent to the * following: * * @code{.cpp} * void(Delta delta, void *data, auto succeed, auto fail); * @endcode * * Where: * * * `delta` is the elapsed time. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. * * `succeed` is a function to call when a process terminates with success. * * `fail` is a function to call when a process terminates with errors. * * The signature of the function call operator of both `succeed` and `fail` * is equivalent to the following: * * @code{.cpp} * void(); * @endcode * * Usually users shouldn't worry about creating adaptors. A scheduler will * create them internally each and avery time a lambda or a functor is used as * a process. * * @sa process * @sa scheduler * * @tparam Func Actual type of process. * @tparam Delta Type to use to provide elapsed time. */ template struct process_adaptor: process, Delta>, private Func { /** * @brief Constructs a process adaptor from a lambda or a functor. * @tparam Args Types of arguments to use to initialize the actual process. * @param args Parameters to use to initialize the actual process. */ template process_adaptor(Args &&...args) : Func{std::forward(args)...} {} /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void update(const Delta delta, void *data) { Func::operator()( delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); } }; } // namespace entt #endif // #include "process/scheduler.hpp" #ifndef ENTT_PROCESS_SCHEDULER_HPP #define ENTT_PROCESS_SCHEDULER_HPP #include #include #include #include #include #include // #include "process.hpp" #ifndef ENTT_PROCESS_PROCESS_HPP #define ENTT_PROCESS_PROCESS_HPP #include #include #include 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 : std::uint8_t { uninitialized = 0, running, paused, succeeded, failed, aborted, finished, rejected }; template auto next(std::integral_constant) -> decltype(std::declval().init(), void()) { static_cast(this)->init(); } template auto next(std::integral_constant, Delta delta, void *data) -> decltype(std::declval().update(delta, data), void()) { static_cast(this)->update(delta, data); } template auto next(std::integral_constant) -> decltype(std::declval().succeeded(), void()) { static_cast(this)->succeeded(); } template auto next(std::integral_constant) -> decltype(std::declval().failed(), void()) { static_cast(this)->failed(); } template auto next(std::integral_constant) -> decltype(std::declval().aborted(), void()) { static_cast(this)->aborted(); } void next(...) const 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() 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() 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() 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() 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() noexcept { static_assert(std::is_base_of_v, "Incorrect use of the class template"); } /** * @brief Aborts a process if it's still alive. * * The function is idempotent and it does nothing if the process isn't * alive. * * @param immediately Requests an immediate operation. */ void abort(const bool immediately = false) { if(alive()) { current = state::aborted; if(immediately) { tick({}); } } } /** * @brief Returns true if a process is either running or paused. * @return True if the process is still alive, false otherwise. */ [[nodiscard]] bool alive() const noexcept { return current == state::running || current == state::paused; } /** * @brief Returns true if a process is already terminated. * @return True if the process is terminated, false otherwise. */ [[nodiscard]] bool finished() const noexcept { return current == state::finished; } /** * @brief Returns true if a process is currently paused. * @return True if the process is paused, false otherwise. */ [[nodiscard]] bool paused() const noexcept { return current == state::paused; } /** * @brief Returns true if a process terminated with errors. * @return True if the process terminated with errors, false otherwise. */ [[nodiscard]] bool rejected() const noexcept { return current == state::rejected; } /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void tick(const Delta delta, void *data = nullptr) { switch(current) { case state::uninitialized: next(std::integral_constant{}); current = state::running; break; case state::running: next(std::integral_constant{}, delta, data); break; default: // suppress warnings break; } // if it's dead, it must be notified and removed immediately switch(current) { case state::succeeded: next(std::integral_constant{}); current = state::finished; break; case state::failed: next(std::integral_constant{}); current = state::rejected; break; case state::aborted: next(std::integral_constant{}); current = state::rejected; break; default: // suppress warnings break; } } private: state current{state::uninitialized}; }; /** * @brief Adaptor for lambdas and functors to turn them into processes. * * Lambdas and functors can't be used directly with a scheduler for they are not * properly defined processes with managed life cycles.
* This class helps in filling the gap and turning lambdas and functors into * full featured processes usable by a scheduler. * * The signature of the function call operator should be equivalent to the * following: * * @code{.cpp} * void(Delta delta, void *data, auto succeed, auto fail); * @endcode * * Where: * * * `delta` is the elapsed time. * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. * * `succeed` is a function to call when a process terminates with success. * * `fail` is a function to call when a process terminates with errors. * * The signature of the function call operator of both `succeed` and `fail` * is equivalent to the following: * * @code{.cpp} * void(); * @endcode * * Usually users shouldn't worry about creating adaptors. A scheduler will * create them internally each and avery time a lambda or a functor is used as * a process. * * @sa process * @sa scheduler * * @tparam Func Actual type of process. * @tparam Delta Type to use to provide elapsed time. */ template struct process_adaptor: process, Delta>, private Func { /** * @brief Constructs a process adaptor from a lambda or a functor. * @tparam Args Types of arguments to use to initialize the actual process. * @param args Parameters to use to initialize the actual process. */ template process_adaptor(Args &&...args) : Func{std::forward(args)...} {} /** * @brief Updates a process and its internal state if required. * @param delta Elapsed time. * @param data Optional data. */ void update(const Delta delta, void *data) { Func::operator()( delta, data, [this]() { this->succeed(); }, [this]() { this->fail(); }); } }; } // namespace entt #endif namespace entt { /** * @brief Cooperative scheduler for processes. * * A cooperative scheduler runs processes and helps managing their life cycles. * * Each process is invoked once per tick. If a process terminates, it's * removed automatically from the scheduler and it's never invoked again.
* A process can also have a child. In this case, the process is replaced with * its child when it terminates if it returns with success. In case of errors, * both the process and its child are discarded. * * Example of use (pseudocode): * * @code{.cpp} * scheduler.attach([](auto delta, void *, auto succeed, auto fail) { * // code * }).then(arguments...); * @endcode * * In order to invoke all scheduled processes, call the `update` member function * passing it the elapsed time to forward to the tasks. * * @sa process * * @tparam Delta Type to use to provide elapsed time. */ template class scheduler { struct process_handler { using instance_type = std::unique_ptr; using update_fn_type = bool(scheduler &, std::size_t, Delta, void *); using abort_fn_type = void(scheduler &, std::size_t, 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) noexcept : handler{ref} {} template continuation then(Args &&...args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; handler->next.reset(new process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); handler = handler->next.get(); return *this; } template continuation then(Func &&func) { return then, Delta>>(std::forward(func)); } private: process_handler *handler; }; template [[nodiscard]] static bool update(scheduler &owner, std::size_t pos, const Delta delta, void *data) { auto *process = static_cast(owner.handlers[pos].instance.get()); process->tick(delta, data); if(process->rejected()) { return true; } else if(process->finished()) { if(auto &&handler = owner.handlers[pos]; handler.next) { handler = std::move(*handler.next); // forces the process to exit the uninitialized state return handler.update(owner, pos, {}, nullptr); } return true; } return false; } template static void abort(scheduler &owner, std::size_t pos, const bool immediately) { static_cast(owner.handlers[pos].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() : handlers{} {} /*! @brief Default move constructor. */ scheduler(scheduler &&) = default; /*! @brief Default move assignment operator. @return This scheduler. */ scheduler &operator=(scheduler &&) = default; /** * @brief Number of processes currently scheduled. * @return Number of processes currently scheduled. */ [[nodiscard]] size_type size() const noexcept { return handlers.size(); } /** * @brief Returns true if at least a process is currently scheduled. * @return True if there are scheduled processes, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handlers.empty(); } /** * @brief Discards all scheduled processes. * * Processes aren't aborted. They are discarded along with their children * and never executed again. */ void clear() { handlers.clear(); } /** * @brief Schedules a process for the next tick. * * Returned value is an opaque object that can be used to attach a child to * the given process. The child is automatically scheduled when the process * terminates and only if the process returns with success. * * Example of use (pseudocode): * * @code{.cpp} * // schedules a task in the form of a process class * scheduler.attach(arguments...) * // appends a child in the form of a lambda function * .then([](auto delta, void *, auto succeed, auto fail) { * // code * }) * // appends a child in the form of another process class * .then(); * @endcode * * @tparam Proc Type of process to schedule. * @tparam Args Types of arguments to use to initialize the process. * @param args Parameters to use to initialize the process. * @return An opaque object to use to concatenate processes. */ template auto attach(Args &&...args) { static_assert(std::is_base_of_v, Proc>, "Invalid process type"); auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; auto &&ref = handlers.emplace_back(process_handler{std::move(proc), &scheduler::update, &scheduler::abort, nullptr}); // forces the process to exit the uninitialized state ref.update(*this, handlers.size() - 1u, {}, nullptr); return continuation{&handlers.back()}; } /** * @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) { for(auto pos = handlers.size(); pos; --pos) { const auto curr = pos - 1u; if(const auto dead = handlers[curr].update(*this, curr, delta, data); dead) { std::swap(handlers[curr], handlers.back()); handlers.pop_back(); } } } /** * @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) { for(auto pos = handlers.size(); pos; --pos) { const auto curr = pos - 1u; handlers[curr].abort(*this, curr, immediately); } } private: std::vector handlers{}; }; } // namespace entt #endif // #include "resource/cache.hpp" #ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP #define ENTT_RESOURCE_RESOURCE_CACHE_HPP #include #include #include #include #include #include #include // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_RESOURCE_FWD_HPP #define ENTT_RESOURCE_FWD_HPP #include namespace entt { template struct resource_loader; template, typename = std::allocator> class resource_cache; template class resource; } // namespace entt #endif // #include "loader.hpp" #ifndef ENTT_RESOURCE_LOADER_HPP #define ENTT_RESOURCE_LOADER_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @brief Transparent loader for shared resources. * @tparam Type Type of resources created by the loader. */ template struct resource_loader { /*! @brief Result type. */ using result_type = std::shared_ptr; /** * @brief Constructs a shared pointer to a resource from its arguments. * @tparam Args Types of arguments to use to construct the resource. * @param args Parameters to use to construct the resource. * @return A shared pointer to a resource of the given type. */ template result_type operator()(Args &&...args) const { return std::make_shared(std::forward(args)...); } }; } // namespace entt #endif // #include "resource.hpp" #ifndef ENTT_RESOURCE_RESOURCE_HPP #define ENTT_RESOURCE_RESOURCE_HPP #include #include #include // #include "fwd.hpp" namespace entt { /** * @brief Basic resource handle. * * A handle wraps a resource and extends its lifetime. It also shares the same * resource with all other handles constructed from the same element.
* As a rule of thumb, resources should never be copied nor moved. Handles are * the way to go to push references around. * * @tparam Type Type of resource managed by a handle. */ template class resource { /*! @brief Resource handles are friends with each other. */ template friend class resource; template static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; public: /*! @brief Resource type. */ using element_type = Type; /*! @brief Handle type. */ using handle_type = std::shared_ptr; /*! @brief Default constructor. */ resource() noexcept : value{} {} /** * @brief Creates a handle from a weak pointer, namely a resource. * @param res A weak pointer to a resource. */ explicit resource(handle_type res) noexcept : value{std::move(res)} {} /*! @brief Default copy constructor. */ resource(const resource &) noexcept = default; /*! @brief Default move constructor. */ resource(resource &&) noexcept = default; /** * @brief Aliasing constructor. * @tparam Other Type of resource managed by the received handle. * @param other The handle with which to share ownership information. * @param res Unrelated and unmanaged resources. */ template resource(const resource &other, element_type &res) noexcept : value{other.value, std::addressof(res)} {} /** * @brief Copy constructs a handle which shares ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. */ template>> resource(const resource &other) noexcept : value{other.value} {} /** * @brief Move constructs a handle which takes ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. */ template>> resource(resource &&other) noexcept : value{std::move(other.value)} {} /** * @brief Default copy assignment operator. * @return This resource handle. */ resource &operator=(const resource &) noexcept = default; /** * @brief Default move assignment operator. * @return This resource handle. */ resource &operator=(resource &&) noexcept = default; /** * @brief Copy assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. * @return This resource handle. */ template std::enable_if_t, resource &> operator=(const resource &other) noexcept { value = other.value; return *this; } /** * @brief Move assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. * @return This resource handle. */ template std::enable_if_t, resource &> operator=(resource &&other) noexcept { value = std::move(other.value); return *this; } /** * @brief Returns a reference to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A reference to the managed resource. */ [[nodiscard]] element_type &operator*() const noexcept { return *value; } /*! @copydoc operator* */ [[nodiscard]] operator element_type &() const noexcept { return *value; } /** * @brief Returns a pointer to the managed resource. * @return A pointer to the managed resource. */ [[nodiscard]] element_type *operator->() const noexcept { return value.get(); } /** * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(value); } /** * @brief Returns the underlying resource handle. * @return The underlying resource handle. */ [[nodiscard]] const handle_type &handle() const noexcept { return value; } private: handle_type value; }; /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if both handles refer to the same resource, false otherwise. */ template [[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return False if both handles refer to the same registry, true otherwise. */ template [[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than the second, false otherwise. */ template [[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) < std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than the second, false otherwise. */ template [[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) > std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than or equal to the second, false * otherwise. */ template [[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { return !(lhs > rhs); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { return !(lhs < rhs); } } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template class resource_cache_iterator final { template friend class resource_cache_iterator; public: using value_type = std::pair>; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr resource_cache_iterator() noexcept = default; constexpr resource_cache_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr resource_cache_iterator(const resource_cache_iterator, Other> &other) noexcept : it{other.it} {} constexpr resource_cache_iterator &operator++() noexcept { return ++it, *this; } constexpr resource_cache_iterator operator++(int) noexcept { resource_cache_iterator orig = *this; return ++(*this), orig; } constexpr resource_cache_iterator &operator--() noexcept { return --it, *this; } constexpr resource_cache_iterator operator--(int) noexcept { resource_cache_iterator orig = *this; return operator--(), orig; } constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { resource_cache_iterator copy = *this; return (copy += value); } constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].first, resource{it[value].second}}; } [[nodiscard]] constexpr reference operator*() const noexcept { return (*this)[0]; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } template friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; template friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; template friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { return !(lhs < rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic cache for resources of any type. * @tparam Type Type of resources managed by a cache. * @tparam Loader Type of loader used to create the resources. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class resource_cache { using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v, "Invalid value type"); using container_allocator = typename alloc_traits::template rebind_alloc>; using container_type = dense_map, container_allocator>; public: /*! @brief Resource type. */ using value_type = Type; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Loader type. */ using loader_type = Loader; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::resource_cache_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::resource_cache_iterator; /*! @brief Default constructor. */ resource_cache() : resource_cache{loader_type{}} {} /** * @brief Constructs an empty cache with a given allocator. * @param allocator The allocator to use. */ explicit resource_cache(const allocator_type &allocator) : resource_cache{loader_type{}, allocator} {} /** * @brief Constructs an empty cache with a given allocator and loader. * @param callable The loader to use. * @param allocator The allocator to use. */ explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) : pool{container_type{allocator}, callable} {} /*! @brief Default copy constructor. */ resource_cache(const resource_cache &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ resource_cache(const resource_cache &other, const allocator_type &allocator) : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} /*! @brief Default move constructor. */ resource_cache(resource_cache &&) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ resource_cache(resource_cache &&other, const allocator_type &allocator) : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} /** * @brief Default copy assignment operator. * @return This cache. */ resource_cache &operator=(const resource_cache &) = default; /** * @brief Default move assignment operator. * @return This cache. */ resource_cache &operator=(resource_cache &&) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return pool.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the cache. If the * cache is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal cache. */ [[nodiscard]] const_iterator cbegin() const noexcept { return pool.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return pool.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the cache. Attempting to dereference the returned iterator results in * undefined behavior. * * @return An iterator to the element following the last instance of the * internal cache. */ [[nodiscard]] const_iterator cend() const noexcept { return pool.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return pool.first().end(); } /** * @brief Returns true if a cache contains no resources, false otherwise. * @return True if the cache contains no resources, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return pool.first().empty(); } /** * @brief Number of resources managed by a cache. * @return Number of resources currently stored. */ [[nodiscard]] size_type size() const noexcept { return pool.first().size(); } /*! @brief Clears a cache. */ void clear() noexcept { pool.first().clear(); } /** * @brief Loads a resource, if its identifier does not exist. * * Arguments are forwarded directly to the loader and _consumed_ only if the * resource doesn't already exist. * * @warning * If the resource isn't loaded correctly, the returned handle could be * invalid and any use of it will result in undefined behavior. * * @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 pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair load(const id_type id, Args &&...args) { if(auto it = pool.first().find(id); it != pool.first().end()) { return {it, false}; } return pool.first().emplace(id, pool.second()(std::forward(args)...)); } /** * @brief Force loads a resource, if its identifier does not exist. * @copydetails load */ template std::pair force_load(const id_type id, Args &&...args) { return {pool.first().insert_or_assign(id, pool.second()(std::forward(args)...)).first, true}; } /** * @brief Returns a handle for a given resource identifier. * * @warning * There is no guarantee that the returned handle is valid.
* If it is not, any use will result in indefinite behavior. * * @param id Unique resource identifier. * @return A handle for the given resource. */ [[nodiscard]] resource operator[](const id_type id) const { if(auto it = pool.first().find(id); it != pool.first().cend()) { return resource{it->second}; } return {}; } /*! @copydoc operator[] */ [[nodiscard]] resource operator[](const id_type id) { if(auto it = pool.first().find(id); it != pool.first().end()) { return resource{it->second}; } return {}; } /** * @brief Checks if a cache contains a given identifier. * @param id Unique resource identifier. * @return True if the cache contains the resource, false otherwise. */ [[nodiscard]] bool contains(const id_type id) const { return pool.first().contains(id); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto it = pool.first().begin(); return pool.first().erase(it + (pos - const_iterator{it})); } /** * @brief Removes the given elements from a cache. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto it = pool.first().begin(); return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); } /** * @brief Removes the given elements from a cache. * @param id Unique resource identifier. * @return Number of resources erased (either 0 or 1). */ size_type erase(const id_type id) { return pool.first().erase(id); } /** * @brief Returns the loader used to create resources. * @return The loader used to create resources. */ [[nodiscard]] loader_type loader() const { return pool.second(); } private: compressed_pair pool; }; } // namespace entt #endif // #include "resource/loader.hpp" #ifndef ENTT_RESOURCE_LOADER_HPP #define ENTT_RESOURCE_LOADER_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @brief Transparent loader for shared resources. * @tparam Type Type of resources created by the loader. */ template struct resource_loader { /*! @brief Result type. */ using result_type = std::shared_ptr; /** * @brief Constructs a shared pointer to a resource from its arguments. * @tparam Args Types of arguments to use to construct the resource. * @param args Parameters to use to construct the resource. * @return A shared pointer to a resource of the given type. */ template result_type operator()(Args &&...args) const { return std::make_shared(std::forward(args)...); } }; } // namespace entt #endif // #include "resource/resource.hpp" #ifndef ENTT_RESOURCE_RESOURCE_HPP #define ENTT_RESOURCE_RESOURCE_HPP #include #include #include // #include "fwd.hpp" namespace entt { /** * @brief Basic resource handle. * * A handle wraps a resource and extends its lifetime. It also shares the same * resource with all other handles constructed from the same element.
* As a rule of thumb, resources should never be copied nor moved. Handles are * the way to go to push references around. * * @tparam Type Type of resource managed by a handle. */ template class resource { /*! @brief Resource handles are friends with each other. */ template friend class resource; template static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; public: /*! @brief Resource type. */ using element_type = Type; /*! @brief Handle type. */ using handle_type = std::shared_ptr; /*! @brief Default constructor. */ resource() noexcept : value{} {} /** * @brief Creates a handle from a weak pointer, namely a resource. * @param res A weak pointer to a resource. */ explicit resource(handle_type res) noexcept : value{std::move(res)} {} /*! @brief Default copy constructor. */ resource(const resource &) noexcept = default; /*! @brief Default move constructor. */ resource(resource &&) noexcept = default; /** * @brief Aliasing constructor. * @tparam Other Type of resource managed by the received handle. * @param other The handle with which to share ownership information. * @param res Unrelated and unmanaged resources. */ template resource(const resource &other, element_type &res) noexcept : value{other.value, std::addressof(res)} {} /** * @brief Copy constructs a handle which shares ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. */ template>> resource(const resource &other) noexcept : value{other.value} {} /** * @brief Move constructs a handle which takes ownership of the resource. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. */ template>> resource(resource &&other) noexcept : value{std::move(other.value)} {} /** * @brief Default copy assignment operator. * @return This resource handle. */ resource &operator=(const resource &) noexcept = default; /** * @brief Default move assignment operator. * @return This resource handle. */ resource &operator=(resource &&) noexcept = default; /** * @brief Copy assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to copy from. * @return This resource handle. */ template std::enable_if_t, resource &> operator=(const resource &other) noexcept { value = other.value; return *this; } /** * @brief Move assignment operator from foreign handle. * @tparam Other Type of resource managed by the received handle. * @param other The handle to move from. * @return This resource handle. */ template std::enable_if_t, resource &> operator=(resource &&other) noexcept { value = std::move(other.value); return *this; } /** * @brief Returns a reference to the managed resource. * * @warning * The behavior is undefined if the handle doesn't contain a resource. * * @return A reference to the managed resource. */ [[nodiscard]] element_type &operator*() const noexcept { return *value; } /*! @copydoc operator* */ [[nodiscard]] operator element_type &() const noexcept { return *value; } /** * @brief Returns a pointer to the managed resource. * @return A pointer to the managed resource. */ [[nodiscard]] element_type *operator->() const noexcept { return value.get(); } /** * @brief Returns true if a handle contains a resource, false otherwise. * @return True if the handle contains a resource, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(value); } /** * @brief Returns the underlying resource handle. * @return The underlying resource handle. */ [[nodiscard]] const handle_type &handle() const noexcept { return value; } private: handle_type value; }; /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if both handles refer to the same resource, false otherwise. */ template [[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) == std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return False if both handles refer to the same registry, true otherwise. */ template [[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than the second, false otherwise. */ template [[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) < std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than the second, false otherwise. */ template [[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { return (std::addressof(*lhs) > std::addressof(*rhs)); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is less than or equal to the second, false * otherwise. */ template [[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { return !(lhs > rhs); } /** * @brief Compares two handles. * @tparam Res Type of resource managed by the first handle. * @tparam Other Type of resource managed by the second handle. * @param lhs A valid handle. * @param rhs A valid handle. * @return True if the first handle is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { return !(lhs < rhs); } } // namespace entt #endif // #include "signal/delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_SIGNAL_FWD_HPP #define ENTT_SIGNAL_FWD_HPP #include namespace entt { template class delegate; template> class basic_dispatcher; template> class emitter; class connection; struct scoped_connection; template class sink; template> class sigh; /*! @brief Alias declaration for the most common use case. */ using dispatcher = basic_dispatcher<>; /*! @brief Disambiguation tag for constructors and the like. */ template struct connect_arg_t { /*! @brief Default constructor. */ explicit connect_arg_t() = default; }; /** * @brief Constant of type connect_arg_t used to disambiguate calls. * @tparam Candidate Element to connect (likely a free or member function). */ template inline constexpr connect_arg_t connect_arg{}; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { return std::index_sequence_for{}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void *, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** * @brief Constructs a delegate with a given object or payload, if any. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance Optional valid object that fits the purpose. */ template delegate(connect_arg_t, Type &&...value_or_instance) noexcept { connect(std::forward(value_or_instance)...); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { fn = [](const void *, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type *function, const void *payload = nullptr) noexcept { ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() noexcept { instance = nullptr; fn = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void *data() const noexcept { return instance; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(instance, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } private: const void *instance; function_type *fn; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) -> delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type &&) -> delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; } // namespace entt #endif // #include "signal/dispatcher.hpp" #ifndef ENTT_SIGNAL_DISPATCHER_HPP #define ENTT_SIGNAL_DISPATCHER_HPP #include #include #include #include #include #include // #include "../container/dense_map.hpp" #ifndef ENTT_CONTAINER_DENSE_MAP_HPP #define ENTT_CONTAINER_DENSE_MAP_HPP #include #include #include #include #include #include #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" #ifndef ENTT_CONFIG_CONFIG_H #define ENTT_CONFIG_CONFIG_H // #include "version.h" #ifndef ENTT_CONFIG_VERSION_H #define ENTT_CONFIG_VERSION_H // #include "macro.h" #ifndef ENTT_CONFIG_MACRO_H #define ENTT_CONFIG_MACRO_H #define ENTT_STR(arg) #arg #define ENTT_XSTR(arg) ENTT_STR(arg) #endif #define ENTT_VERSION_MAJOR 3 #define ENTT_VERSION_MINOR 11 #define ENTT_VERSION_PATCH 1 #define ENTT_VERSION \ ENTT_XSTR(ENTT_VERSION_MAJOR) \ "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) #endif #if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) # define ENTT_CONSTEXPR # define ENTT_THROW throw # define ENTT_TRY try # define ENTT_CATCH catch(...) #else # define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) # define ENTT_THROW # define ENTT_TRY if(true) # define ENTT_CATCH if(false) #endif #ifdef ENTT_USE_ATOMIC # include # define ENTT_MAYBE_ATOMIC(Type) std::atomic #else # define ENTT_MAYBE_ATOMIC(Type) Type #endif #ifndef ENTT_ID_TYPE # include # define ENTT_ID_TYPE std::uint32_t #endif #ifndef ENTT_SPARSE_PAGE # define ENTT_SPARSE_PAGE 4096 #endif #ifndef ENTT_PACKED_PAGE # define ENTT_PACKED_PAGE 1024 #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT # define ENTT_ASSERT(condition, msg) (void(0)) #elif !defined ENTT_ASSERT # include # define ENTT_ASSERT(condition, msg) assert(condition) #endif #ifdef ENTT_DISABLE_ASSERT # undef ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) #elif !defined ENTT_ASSERT_CONSTEXPR # define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) #endif #ifdef ENTT_NO_ETO # define ENTT_ETO_TYPE(Type) void #else # define ENTT_ETO_TYPE(Type) Type #endif #ifdef ENTT_STANDARD_CPP # define ENTT_NONSTD false #else # define ENTT_NONSTD true # if defined __clang__ || defined __GNUC__ # define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ # define ENTT_PRETTY_FUNCTION_PREFIX '=' # define ENTT_PRETTY_FUNCTION_SUFFIX ']' # elif defined _MSC_VER # define ENTT_PRETTY_FUNCTION __FUNCSIG__ # define ENTT_PRETTY_FUNCTION_PREFIX '<' # define ENTT_PRETTY_FUNCTION_SUFFIX '>' # endif #endif #if defined _MSC_VER # pragma detect_mismatch("entt.version", ENTT_VERSION) # pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) # pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) # pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) #endif #endif // #include "fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/iterator.hpp" #ifndef ENTT_CORE_ITERATOR_HPP #define ENTT_CORE_ITERATOR_HPP #include #include #include #include namespace entt { /** * @brief Helper type to use as pointer with input iterators. * @tparam Type of wrapped value. */ template struct input_iterator_pointer final { /*! @brief Value type. */ using value_type = Type; /*! @brief Pointer type. */ using pointer = Type *; /*! @brief Reference type. */ using reference = Type &; /** * @brief Constructs a proxy object by move. * @param val Value to use to initialize the proxy object. */ constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) : value{std::move(val)} {} /** * @brief Access operator for accessing wrapped values. * @return A pointer to the wrapped value. */ [[nodiscard]] constexpr pointer operator->() noexcept { return std::addressof(value); } /** * @brief Dereference operator for accessing wrapped values. * @return A reference to the wrapped value. */ [[nodiscard]] constexpr reference operator*() noexcept { return value; } private: Type value; }; /** * @brief Plain iota iterator (waiting for C++20). * @tparam Type Value type. */ template class iota_iterator final { static_assert(std::is_integral_v, "Not an integral type"); public: /*! @brief Value type, likely an integral one. */ using value_type = Type; /*! @brief Invalid pointer type. */ using pointer = void; /*! @brief Non-reference type, same as value type. */ using reference = value_type; /*! @brief Difference type. */ using difference_type = std::ptrdiff_t; /*! @brief Iterator category. */ using iterator_category = std::input_iterator_tag; /*! @brief Default constructor. */ constexpr iota_iterator() noexcept : current{} {} /** * @brief Constructs an iota iterator from a given value. * @param init The initial value assigned to the iota iterator. */ constexpr iota_iterator(const value_type init) noexcept : current{init} {} /** * @brief Pre-increment operator. * @return This iota iterator. */ constexpr iota_iterator &operator++() noexcept { return ++current, *this; } /** * @brief Post-increment operator. * @return This iota iterator. */ constexpr iota_iterator operator++(int) noexcept { iota_iterator orig = *this; return ++(*this), orig; } /** * @brief Dereference operator. * @return The underlying value. */ [[nodiscard]] constexpr reference operator*() const noexcept { return current; } private: value_type current; }; /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return *lhs == *rhs; } /** * @brief Comparison operator. * @tparam Type Value type of the iota iterator. * @param lhs A properly initialized iota iterator. * @param rhs A properly initialized iota iterator. * @return True if the two iterators differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { return !(lhs == rhs); } /** * @brief Utility class to create an iterable object from a pair of iterators. * @tparam It Type of iterator. * @tparam Sentinel Type of sentinel. */ template struct iterable_adaptor final { /*! @brief Value type. */ using value_type = typename std::iterator_traits::value_type; /*! @brief Iterator type. */ using iterator = It; /*! @brief Sentinel type. */ using sentinel = Sentinel; /*! @brief Default constructor. */ constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first{}, last{} {} /** * @brief Creates an iterable object from a pair of iterators. * @param from Begin iterator. * @param to End iterator. */ constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) : first{std::move(from)}, last{std::move(to)} {} /** * @brief Returns an iterator to the beginning. * @return An iterator to the first element of the range. */ [[nodiscard]] constexpr iterator begin() const noexcept { return first; } /** * @brief Returns an iterator to the end. * @return An iterator to the element following the last element of the * range. */ [[nodiscard]] constexpr sentinel end() const noexcept { return last; } /*! @copydoc begin */ [[nodiscard]] constexpr iterator cbegin() const noexcept { return begin(); } /*! @copydoc end */ [[nodiscard]] constexpr sentinel cend() const noexcept { return end(); } private: It first; Sentinel last; }; } // namespace entt #endif // #include "../core/memory.hpp" #ifndef ENTT_CORE_MEMORY_HPP #define ENTT_CORE_MEMORY_HPP #include #include #include #include #include #include // #include "../config/config.h" namespace entt { /** * @brief Checks whether a value is a power of two or not. * @param value A value that may or may not be a power of two. * @return True if the value is a power of two, false otherwise. */ [[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { return value && ((value & (value - 1)) == 0); } /** * @brief Computes the smallest power of two greater than or equal to a value. * @param value The value to use. * @return The smallest power of two greater than or equal to the given value. */ [[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); std::size_t curr = value - (value != 0u); for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { curr |= curr >> next; } return ++curr; } /** * @brief Fast module utility function (powers of two only). * @param value A value for which to calculate the modulus. * @param mod _Modulus_, it must be a power of two. * @return The common remainder. */ [[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); return value & (mod - 1u); } /** * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). * @tparam Type Pointer type. * @param ptr Fancy or raw pointer. * @return A raw pointer that represents the address of the original pointer. */ template [[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { if constexpr(std::is_pointer_v>) { return ptr; } else { return to_address(std::forward(ptr).operator->()); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { lhs = rhs; } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { lhs = std::move(rhs); } } /** * @brief Utility function to design allocation-aware containers. * @tparam Allocator Type of allocator. * @param lhs A valid allocator. * @param rhs Another valid allocator. */ template constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { if constexpr(std::allocator_traits::propagate_on_container_swap::value) { using std::swap; swap(lhs, rhs); } else { ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); } } /** * @brief Deleter for allocator-aware unique pointers (waiting for C++20). * @tparam Args Types of arguments to use to construct the object. */ template struct allocation_deleter: private Allocator { /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Pointer type. */ using pointer = typename std::allocator_traits::pointer; /** * @brief Inherited constructors. * @param alloc The allocator to use. */ constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) : Allocator{alloc} {} /** * @brief Destroys the pointed object and deallocates its memory. * @param ptr A valid pointer to an object of the given type. */ constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { using alloc_traits = typename std::allocator_traits; alloc_traits::destroy(*this, to_address(ptr)); alloc_traits::deallocate(*this, ptr, 1u); } }; /** * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). * @tparam Type Type of object to allocate for and to construct. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A properly initialized unique pointer with a custom deleter. */ template ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { static_assert(!std::is_array_v, "Array types are not supported"); using alloc_traits = typename std::allocator_traits::template rebind_traits; using allocator_type = typename alloc_traits::allocator_type; allocator_type alloc{allocator}; auto ptr = alloc_traits::allocate(alloc, 1u); ENTT_TRY { alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); } ENTT_CATCH { alloc_traits::deallocate(alloc, ptr, 1u); ENTT_THROW; } return std::unique_ptr>{ptr, alloc}; } /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct uses_allocator_construction { template static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { if constexpr(!std::uses_allocator_v && std::is_constructible_v) { return std::forward_as_tuple(std::forward(params)...); } else { static_assert(std::uses_allocator_v, "Ill-formed request"); if constexpr(std::is_constructible_v) { return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; } else { static_assert(std::is_constructible_v, "Ill-formed request"); return std::forward_as_tuple(std::forward(params)..., allocator); } } } }; template struct uses_allocator_construction> { using type = std::pair; template static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { return std::make_tuple( std::piecewise_construct, std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); } template static constexpr auto args(const Allocator &allocator) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); } template static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); } template static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); } template static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Prepares the argument list needed to * create an object of a given type by means of uses-allocator construction. * * @tparam Type Type to return arguments for. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return The arguments needed to create an object of the given type. */ template constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { return internal::uses_allocator_construction::args(allocator, std::forward(args)...); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A newly created object of the given type. */ template constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } /** * @brief Uses-allocator construction utility (waiting for C++20). * * Primarily intended for internal use. Creates an object of a given type by * means of uses-allocator construction at an uninitialized memory location. * * @tparam Type Type of object to create. * @tparam Allocator Type of allocator used to manage memory and elements. * @tparam Args Types of arguments to use to construct the object. * @param value Memory location in which to place the object. * @param allocator The allocator to use. * @param args Parameters to use to construct the object. * @return A pointer to the newly created object of the given type. */ template constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); } } // namespace entt #endif // #include "../core/type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif // #include "fwd.hpp" #ifndef ENTT_CONTAINER_FWD_HPP #define ENTT_CONTAINER_FWD_HPP #include #include namespace entt { template< typename Key, typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator>> class dense_map; template< typename Type, typename = std::hash, typename = std::equal_to, typename = std::allocator> class dense_set; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct dense_map_node final { using value_type = std::pair; template dense_map_node(const std::size_t pos, Args &&...args) : next{pos}, element{std::forward(args)...} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const std::size_t pos, Args &&...args) : next{pos}, element{entt::make_obj_using_allocator(allocator, std::forward(args)...)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, const dense_map_node &other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, other.element)} {} template dense_map_node(std::allocator_arg_t, const Allocator &allocator, dense_map_node &&other) : next{other.next}, element{entt::make_obj_using_allocator(allocator, std::move(other.element))} {} std::size_t next; value_type element; }; template class dense_map_iterator final { template friend class dense_map_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_iterator() noexcept : it{} {} constexpr dense_map_iterator(const It iter) noexcept : it{iter} {} template && std::is_constructible_v>> constexpr dense_map_iterator(const dense_map_iterator &other) noexcept : it{other.it} {} constexpr dense_map_iterator &operator++() noexcept { return ++it, *this; } constexpr dense_map_iterator operator++(int) noexcept { dense_map_iterator orig = *this; return ++(*this), orig; } constexpr dense_map_iterator &operator--() noexcept { return --it, *this; } constexpr dense_map_iterator operator--(int) noexcept { dense_map_iterator orig = *this; return operator--(), orig; } constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { it += value; return *this; } constexpr dense_map_iterator operator+(const difference_type value) const noexcept { dense_map_iterator copy = *this; return (copy += value); } constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { return (*this += -value); } constexpr dense_map_iterator operator-(const difference_type value) const noexcept { return (*this + -value); } [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { return {it[value].element.first, it[value].element.second}; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it->element.first, it->element.second}; } template friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; template friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; private: It it; }; template [[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it - rhs.it; } template [[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it == rhs.it; } template [[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs == rhs); } template [[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return lhs.it < rhs.it; } template [[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return rhs < lhs; } template [[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs > rhs); } template [[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { return !(lhs < rhs); } template class dense_map_local_iterator final { template friend class dense_map_local_iterator; using first_type = decltype(std::as_const(std::declval()->element.first)); using second_type = decltype((std::declval()->element.second)); public: using value_type = std::pair; using pointer = input_iterator_pointer; using reference = value_type; using difference_type = std::ptrdiff_t; using iterator_category = std::input_iterator_tag; constexpr dense_map_local_iterator() noexcept : it{}, offset{} {} constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept : it{iter}, offset{pos} {} template && std::is_constructible_v>> constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept : it{other.it}, offset{other.offset} {} constexpr dense_map_local_iterator &operator++() noexcept { return offset = it[offset].next, *this; } constexpr dense_map_local_iterator operator++(int) noexcept { dense_map_local_iterator orig = *this; return ++(*this), orig; } [[nodiscard]] constexpr pointer operator->() const noexcept { return operator*(); } [[nodiscard]] constexpr reference operator*() const noexcept { return {it[offset].element.first, it[offset].element.second}; } [[nodiscard]] constexpr std::size_t index() const noexcept { return offset; } private: It it; std::size_t offset; }; template [[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return lhs.index() == rhs.index(); } template [[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { return !(lhs == rhs); } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Associative container for key-value pairs with unique keys. * * Internally, elements are organized into buckets. Which bucket an element is * placed into depends entirely on the hash of its key. Keys with the same hash * code appear in the same bucket. * * @tparam Key Key type of the associative container. * @tparam Type Mapped type of the associative container. * @tparam Hash Type of function to use to hash the keys. * @tparam KeyEqual Type of function to use to compare the keys for equality. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class dense_map { static constexpr float default_threshold = 0.875f; static constexpr std::size_t minimum_capacity = 8u; using node_type = internal::dense_map_node; using alloc_traits = typename std::allocator_traits; static_assert(std::is_same_v>, "Invalid value type"); using sparse_container_type = std::vector>; using packed_container_type = std::vector>; template [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { return fast_mod(static_cast(sparse.second()(key)), bucket_count()); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return begin() + static_cast(it.index()); } } return end(); } template [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { if(packed.second()(it->first, key)) { return cbegin() + static_cast(it.index()); } } return cend(); } template [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } template [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { const auto index = key_to_bucket(key); if(auto it = constrained_find(key, index); it != end()) { it->second = std::forward(value); return std::make_pair(it, false); } packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); sparse.first()[index] = packed.first().size() - 1u; rehash_if_required(); return std::make_pair(--end(), true); } void move_and_pop(const std::size_t pos) { if(const auto last = size() - 1u; pos != last) { size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); packed.first()[pos] = std::move(packed.first().back()); for(; *curr != last; curr = &packed.first()[*curr].next) {} *curr = pos; } packed.first().pop_back(); } void rehash_if_required() { if(size() > (bucket_count() * max_load_factor())) { rehash(bucket_count() * 2u); } } public: /*! @brief Key type of the container. */ using key_type = Key; /*! @brief Mapped type of the container. */ using mapped_type = Type; /*! @brief Key-value type of the container. */ using value_type = std::pair; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Type of function to use to hash the keys. */ using hasher = Hash; /*! @brief Type of function to use to compare the keys for equality. */ using key_equal = KeyEqual; /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Input iterator type. */ using iterator = internal::dense_map_iterator; /*! @brief Constant input iterator type. */ using const_iterator = internal::dense_map_iterator; /*! @brief Input iterator type. */ using local_iterator = internal::dense_map_local_iterator; /*! @brief Constant input iterator type. */ using const_local_iterator = internal::dense_map_local_iterator; /*! @brief Default constructor. */ dense_map() : dense_map{minimum_capacity} {} /** * @brief Constructs an empty container with a given allocator. * @param allocator The allocator to use. */ explicit dense_map(const allocator_type &allocator) : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator and user * supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const allocator_type &allocator) : dense_map{cnt, hasher{}, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param allocator The allocator to use. */ dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) : dense_map{cnt, hash, key_equal{}, allocator} {} /** * @brief Constructs an empty container with a given allocator, hash * function, compare function and user supplied minimal number of buckets. * @param cnt Minimal number of buckets. * @param hash Hash function to use. * @param equal Compare function to use. * @param allocator The allocator to use. */ explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) : sparse{allocator, hash}, packed{allocator, equal}, threshold{default_threshold} { rehash(cnt); } /*! @brief Default copy constructor. */ dense_map(const dense_map &) = default; /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ dense_map(const dense_map &other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, threshold{other.threshold} {} /*! @brief Default move constructor. */ dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ dense_map(dense_map &&other, const allocator_type &allocator) : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, threshold{other.threshold} {} /** * @brief Default copy assignment operator. * @return This container. */ dense_map &operator=(const dense_map &) = default; /** * @brief Default move assignment operator. * @return This container. */ dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return sparse.first().get_allocator(); } /** * @brief Returns an iterator to the beginning. * * The returned iterator points to the first instance of the internal array. * If the array is empty, the returned iterator will be equal to `end()`. * * @return An iterator to the first instance of the internal array. */ [[nodiscard]] const_iterator cbegin() const noexcept { return packed.first().begin(); } /*! @copydoc cbegin */ [[nodiscard]] const_iterator begin() const noexcept { return cbegin(); } /*! @copydoc begin */ [[nodiscard]] iterator begin() noexcept { return packed.first().begin(); } /** * @brief Returns an iterator to the end. * * The returned iterator points to the element following the last instance * of the internal array. Attempting to dereference the returned iterator * results in undefined behavior. * * @return An iterator to the element following the last instance of the * internal array. */ [[nodiscard]] const_iterator cend() const noexcept { return packed.first().end(); } /*! @copydoc cend */ [[nodiscard]] const_iterator end() const noexcept { return cend(); } /*! @copydoc end */ [[nodiscard]] iterator end() noexcept { return packed.first().end(); } /** * @brief Checks whether a container is empty. * @return True if the container is empty, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return packed.first().empty(); } /** * @brief Returns the number of elements in a container. * @return Number of elements in a container. */ [[nodiscard]] size_type size() const noexcept { return packed.first().size(); } /** * @brief Returns the maximum possible number of elements. * @return Maximum possible number of elements. */ [[nodiscard]] size_type max_size() const noexcept { return packed.first().max_size(); } /*! @brief Clears the container. */ void clear() noexcept { sparse.first().clear(); packed.first().clear(); rehash(0u); } /** * @brief Inserts an element into the container, if the key does not exist. * @param value A key-value pair eventually convertible to the value type. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ std::pair insert(const value_type &value) { return insert_or_do_nothing(value.first, value.second); } /*! @copydoc insert */ std::pair insert(value_type &&value) { return insert_or_do_nothing(std::move(value.first), std::move(value.second)); } /** * @copydoc insert * @tparam Arg Type of the key-value pair to insert into the container. */ template std::enable_if_t, std::pair> insert(Arg &&value) { return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); } /** * @brief Inserts elements into the container, if their keys do not exist. * @tparam It Type of input iterator. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. */ template void insert(It first, It last) { for(; first != last; ++first) { insert(*first); } } /** * @brief Inserts an element into the container or assigns to the current * element if the key already exists. * @tparam Arg Type of the value to insert or assign. * @param key A key used both to look up and to insert if not found. * @param value A value to insert or assign. * @return A pair consisting of an iterator to the element and a bool * denoting whether the insertion took place. */ template std::pair insert_or_assign(const key_type &key, Arg &&value) { return insert_or_overwrite(key, std::forward(value)); } /*! @copydoc insert_or_assign */ template std::pair insert_or_assign(key_type &&key, Arg &&value) { return insert_or_overwrite(std::move(key), std::forward(value)); } /** * @brief Constructs an element in-place, if the key does not exist. * * The element is also constructed when the container already has the key, * in which case the newly constructed object is destroyed immediately. * * @tparam Args Types of arguments to forward to the constructor of the * element. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair emplace([[maybe_unused]] Args &&...args) { if constexpr(sizeof...(Args) == 0u) { return insert_or_do_nothing(key_type{}); } else if constexpr(sizeof...(Args) == 1u) { return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); } else if constexpr(sizeof...(Args) == 2u) { return insert_or_do_nothing(std::forward(args)...); } else { auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); const auto index = key_to_bucket(node.element.first); if(auto it = constrained_find(node.element.first, index); it != end()) { packed.first().pop_back(); return std::make_pair(it, false); } std::swap(node.next, sparse.first()[index]); rehash_if_required(); return std::make_pair(--end(), true); } } /** * @brief Inserts in-place if the key does not exist, does nothing if the * key exists. * @tparam Args Types of arguments to forward to the constructor of the * element. * @param key A key used both to look up and to insert if not found. * @param args Arguments to forward to the constructor of the element. * @return A pair consisting of an iterator to the inserted element (or to * the element that prevented the insertion) and a bool denoting whether the * insertion took place. */ template std::pair try_emplace(const key_type &key, Args &&...args) { return insert_or_do_nothing(key, std::forward(args)...); } /*! @copydoc try_emplace */ template std::pair try_emplace(key_type &&key, Args &&...args) { return insert_or_do_nothing(std::move(key), std::forward(args)...); } /** * @brief Removes an element from a given position. * @param pos An iterator to the element to remove. * @return An iterator following the removed element. */ iterator erase(const_iterator pos) { const auto diff = pos - cbegin(); erase(pos->first); return begin() + diff; } /** * @brief Removes the given elements from a container. * @param first An iterator to the first element of the range of elements. * @param last An iterator past the last element of the range of elements. * @return An iterator following the last removed element. */ iterator erase(const_iterator first, const_iterator last) { const auto dist = first - cbegin(); for(auto from = last - cbegin(); from != dist; --from) { erase(packed.first()[from - 1u].element.first); } return (begin() + dist); } /** * @brief Removes the element associated with a given key. * @param key A key value of an element to remove. * @return Number of elements removed (either 0 or 1). */ size_type erase(const key_type &key) { for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { if(packed.second()(packed.first()[*curr].element.first, key)) { const auto index = *curr; *curr = packed.first()[*curr].next; move_and_pop(index); return 1u; } } return 0u; } /** * @brief Exchanges the contents with those of a given container. * @param other Container to exchange the content with. */ void swap(dense_map &other) { using std::swap; swap(sparse, other.sparse); swap(packed, other.packed); swap(threshold, other.threshold); } /** * @brief Accesses a given element with bounds checking. * @param key A key of an element to find. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &at(const key_type &key) { auto it = find(key); ENTT_ASSERT(it != end(), "Invalid key"); return it->second; } /*! @copydoc at */ [[nodiscard]] const mapped_type &at(const key_type &key) const { auto it = find(key); ENTT_ASSERT(it != cend(), "Invalid key"); return it->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](const key_type &key) { return insert_or_do_nothing(key).first->second; } /** * @brief Accesses or inserts a given element. * @param key A key of an element to find or insert. * @return A reference to the mapped value of the requested element. */ [[nodiscard]] mapped_type &operator[](key_type &&key) { return insert_or_do_nothing(std::move(key)).first->second; } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ [[nodiscard]] size_type count(const key_type &key) const { return find(key) != end(); } /** * @brief Returns the number of elements matching a key (either 1 or 0). * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return Number of elements matching the key (either 1 or 0). */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> count(const Other &key) const { return find(key) != end(); } /** * @brief Finds an element with a given key. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ [[nodiscard]] iterator find(const key_type &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ [[nodiscard]] const_iterator find(const key_type &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Finds an element with a key that compares _equivalent_ to a given * key. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return An iterator to an element with the given key. If no such element * is found, a past-the-end iterator is returned. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) { return constrained_find(key, key_to_bucket(key)); } /*! @copydoc find */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> find(const Other &key) const { return constrained_find(key, key_to_bucket(key)); } /** * @brief Returns a range containing all elements with a given key. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ [[nodiscard]] std::pair equal_range(const key_type &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ [[nodiscard]] std::pair equal_range(const key_type &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Returns a range containing all elements that compare _equivalent_ * to a given key. * @tparam Other Type of an element to search for. * @param key Key value of an element to search for. * @return A pair of iterators pointing to the first element and past the * last element of the range. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) { const auto it = find(key); return {it, it + !(it == end())}; } /*! @copydoc equal_range */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> equal_range(const Other &key) const { const auto it = find(key); return {it, it + !(it == cend())}; } /** * @brief Checks if the container contains an element with a given key. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ [[nodiscard]] bool contains(const key_type &key) const { return (find(key) != cend()); } /** * @brief Checks if the container contains an element with a key that * compares _equivalent_ to a given value. * @tparam Other Type of the key value of an element to search for. * @param key Key value of an element to search for. * @return True if there is such an element, false otherwise. */ template [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> contains(const Other &key) const { return (find(key) != cend()); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator cbegin(const size_type index) const { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] const_local_iterator begin(const size_type index) const { return cbegin(index); } /** * @brief Returns an iterator to the beginning of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the beginning of the given bucket. */ [[nodiscard]] local_iterator begin(const size_type index) { return {packed.first().begin(), sparse.first()[index]}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] const_local_iterator end(const size_type index) const { return cend(index); } /** * @brief Returns an iterator to the end of a given bucket. * @param index An index of a bucket to access. * @return An iterator to the end of the given bucket. */ [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { return {packed.first().begin(), (std::numeric_limits::max)()}; } /** * @brief Returns the number of buckets. * @return The number of buckets. */ [[nodiscard]] size_type bucket_count() const { return sparse.first().size(); } /** * @brief Returns the maximum number of buckets. * @return The maximum number of buckets. */ [[nodiscard]] size_type max_bucket_count() const { return sparse.first().max_size(); } /** * @brief Returns the number of elements in a given bucket. * @param index The index of the bucket to examine. * @return The number of elements in the given bucket. */ [[nodiscard]] size_type bucket_size(const size_type index) const { return static_cast(std::distance(begin(index), end(index))); } /** * @brief Returns the bucket for a given key. * @param key The value of the key to examine. * @return The bucket for the given key. */ [[nodiscard]] size_type bucket(const key_type &key) const { return key_to_bucket(key); } /** * @brief Returns the average number of elements per bucket. * @return The average number of elements per bucket. */ [[nodiscard]] float load_factor() const { return size() / static_cast(bucket_count()); } /** * @brief Returns the maximum average number of elements per bucket. * @return The maximum average number of elements per bucket. */ [[nodiscard]] float max_load_factor() const { return threshold; } /** * @brief Sets the desired maximum average number of elements per bucket. * @param value A desired maximum average number of elements per bucket. */ void max_load_factor(const float value) { ENTT_ASSERT(value > 0.f, "Invalid load factor"); threshold = value; rehash(0u); } /** * @brief Reserves at least the specified number of buckets and regenerates * the hash table. * @param cnt New number of buckets. */ void rehash(const size_type cnt) { auto value = cnt > minimum_capacity ? cnt : minimum_capacity; const auto cap = static_cast(size() / max_load_factor()); value = value > cap ? value : cap; if(const auto sz = next_power_of_two(value); sz != bucket_count()) { sparse.first().resize(sz); for(auto &&elem: sparse.first()) { elem = std::numeric_limits::max(); } for(size_type pos{}, last = size(); pos < last; ++pos) { const auto index = key_to_bucket(packed.first()[pos].element.first); packed.first()[pos].next = std::exchange(sparse.first()[index], pos); } } } /** * @brief Reserves space for at least the specified number of elements and * regenerates the hash table. * @param cnt New number of elements. */ void reserve(const size_type cnt) { packed.first().reserve(cnt); rehash(static_cast(std::ceil(cnt / max_load_factor()))); } /** * @brief Returns the function used to hash the keys. * @return The function used to hash the keys. */ [[nodiscard]] hasher hash_function() const { return sparse.second(); } /** * @brief Returns the function used to compare keys for equality. * @return The function used to compare keys for equality. */ [[nodiscard]] key_equal key_eq() const { return packed.second(); } private: compressed_pair sparse; compressed_pair packed; float threshold; }; } // namespace entt /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace std { template struct uses_allocator, Allocator> : std::true_type {}; } // namespace std /** * Internal details not to be documented. * @endcond */ #endif // #include "../core/compressed_pair.hpp" #ifndef ENTT_CORE_COMPRESSED_PAIR_HPP #define ENTT_CORE_COMPRESSED_PAIR_HPP #include #include #include #include // #include "type_traits.hpp" #ifndef ENTT_CORE_TYPE_TRAITS_HPP #define ENTT_CORE_TYPE_TRAITS_HPP #include #include #include #include // #include "../config/config.h" // #include "fwd.hpp" namespace entt { /** * @brief Utility class to disambiguate overloaded functions. * @tparam N Number of choices available. */ template struct choice_t // Unfortunately, doxygen cannot parse such a construct. : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ {}; /*! @copybrief choice_t */ template<> struct choice_t<0> {}; /** * @brief Variable template for the choice trick. * @tparam N Number of choices available. */ template inline constexpr choice_t choice{}; /** * @brief Identity type trait. * * Useful to establish non-deduced contexts in template argument deduction * (waiting for C++20) or to provide types through function arguments. * * @tparam Type A type. */ template struct type_identity { /*! @brief Identity type. */ using type = Type; }; /** * @brief Helper type. * @tparam Type A type. */ template using type_identity_t = typename type_identity::type; /** * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. * @tparam Type The type of which to return the size. * @tparam The size of the type if `sizeof` accepts it, 0 otherwise. */ template struct size_of: std::integral_constant {}; /*! @copydoc size_of */ template struct size_of> : std::integral_constant {}; /** * @brief Helper variable template. * @tparam Type The type of which to return the size. */ template inline constexpr std::size_t size_of_v = size_of::value; /** * @brief Using declaration to be used to _repeat_ the same type a number of * times equal to the size of a given parameter pack. * @tparam Type A type to repeat. */ template using unpack_as_type = Type; /** * @brief Helper variable template to be used to _repeat_ the same value a * number of times equal to the size of a given parameter pack. * @tparam Value A value to repeat. */ template inline constexpr auto unpack_as_value = Value; /** * @brief Wraps a static constant. * @tparam Value A static constant. */ template using integral_constant = std::integral_constant; /** * @brief Alias template to facilitate the creation of named values. * @tparam Value A constant value at least convertible to `id_type`. */ template using tag = integral_constant; /** * @brief A class to use to push around lists of types, nothing more. * @tparam Type Types provided by the type list. */ template struct type_list { /*! @brief Type list type. */ using type = type_list; /*! @brief Compile-time number of elements in the type list. */ static constexpr auto size = sizeof...(Type); }; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_element; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Index Index of the type to return. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element> : type_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_element<0u, type_list> { /*! @brief Searched type. */ using type = First; }; /** * @brief Helper type. * @tparam Index Index of the type to return. * @tparam List Type list to search into. */ template using type_list_element_t = typename type_list_element::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_index; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam First First type provided by the type list. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 1u + type_list_index>::value; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. * @tparam Other Other types provided by the type list. */ template struct type_list_index> { static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Provides compile-time type access to the types of a type list. * @tparam Type Type to look for and for which to return the index. */ template struct type_list_index> { /*! @brief Unsigned integer type. */ using value_type = std::size_t; /*! @brief Compile-time position of the given type in the sublist. */ static constexpr value_type value = 0u; }; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for and for which to return the index. */ template inline constexpr std::size_t type_list_index_v = type_list_index::value; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @return A type list composed by the types of both the type lists. */ template constexpr type_list operator+(type_list, type_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct type_list_cat; /*! @brief Concatenates multiple type lists. */ template<> struct type_list_cat<> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list<>; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. * @tparam List Other type lists, if any. */ template struct type_list_cat, type_list, List...> { /*! @brief A type list composed by the types of all the type lists. */ using type = typename type_list_cat, List...>::type; }; /** * @brief Concatenates multiple type lists. * @tparam Type Types provided by the type list. */ template struct type_list_cat> { /*! @brief A type list composed by the types of all the type lists. */ using type = type_list; }; /** * @brief Helper type. * @tparam List Type lists to concatenate. */ template using type_list_cat_t = typename type_list_cat::type; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_unique; /** * @brief Removes duplicates types from a type list. * @tparam Type One of the types provided by the given type list. * @tparam Other The other types provided by the given type list. */ template struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = std::conditional_t< (std::is_same_v || ...), typename type_list_unique>::type, type_list_cat_t, typename type_list_unique>::type>>; }; /*! @brief Removes duplicates types from a type list. */ template<> struct type_list_unique> { /*! @brief A type list without duplicate types. */ using type = type_list<>; }; /** * @brief Helper type. * @tparam Type A type list. */ template using type_list_unique_t = typename type_list_unique::type; /** * @brief Provides the member constant `value` to true if a type list contains a * given type, false otherwise. * @tparam List Type list. * @tparam Type Type to look for. */ template struct type_list_contains; /** * @copybrief type_list_contains * @tparam Type Types provided by the type list. * @tparam Other Type to look for. */ template struct type_list_contains, Other>: std::disjunction...> {}; /** * @brief Helper variable template. * @tparam List Type list. * @tparam Type Type to look for. */ template inline constexpr bool type_list_contains_v = type_list_contains::value; /*! @brief Primary template isn't defined on purpose. */ template struct type_list_diff; /** * @brief Computes the difference between two type lists. * @tparam Type Types provided by the first type list. * @tparam Other Types provided by the second type list. */ template struct type_list_diff, type_list> { /*! @brief A type list that is the difference between the two type lists. */ using type = type_list_cat_t, Type>, type_list<>, type_list>...>; }; /** * @brief Helper type. * @tparam List Type lists between which to compute the difference. */ template using type_list_diff_t = typename type_list_diff::type; /*! @brief Primary template isn't defined on purpose. */ template class> struct type_list_transform; /** * @brief Applies a given _function_ to a type list and generate a new list. * @tparam Type Types provided by the type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> struct type_list_transform, Op> { /*! @brief Resulting type list after applying the transform function. */ using type = type_list::type...>; }; /** * @brief Helper type. * @tparam List Type list. * @tparam Op Unary operation as template class with a type member named `type`. */ template class Op> using type_list_transform_t = typename type_list_transform::type; /** * @brief A class to use to push around lists of constant values, nothing more. * @tparam Value Values provided by the value list. */ template struct value_list { /*! @brief Value list type. */ using type = value_list; /*! @brief Compile-time number of elements in the value list. */ static constexpr auto size = sizeof...(Value); }; /*! @brief Primary template isn't defined on purpose. */ template struct value_list_element; /** * @brief Provides compile-time indexed access to the values of a value list. * @tparam Index Index of the value to return. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element> : value_list_element> {}; /** * @brief Provides compile-time indexed access to the types of a type list. * @tparam Value First value provided by the value list. * @tparam Other Other values provided by the value list. */ template struct value_list_element<0u, value_list> { /*! @brief Searched value. */ static constexpr auto value = Value; }; /** * @brief Helper type. * @tparam Index Index of the value to return. * @tparam List Value list to search into. */ template inline constexpr auto value_list_element_v = value_list_element::value; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @return A value list composed by the values of both the value lists. */ template constexpr value_list operator+(value_list, value_list) { return {}; } /*! @brief Primary template isn't defined on purpose. */ template struct value_list_cat; /*! @brief Concatenates multiple value lists. */ template<> struct value_list_cat<> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list<>; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the first value list. * @tparam Other Values provided by the second value list. * @tparam List Other value lists, if any. */ template struct value_list_cat, value_list, List...> { /*! @brief A value list composed by the values of all the value lists. */ using type = typename value_list_cat, List...>::type; }; /** * @brief Concatenates multiple value lists. * @tparam Value Values provided by the value list. */ template struct value_list_cat> { /*! @brief A value list composed by the values of all the value lists. */ using type = value_list; }; /** * @brief Helper type. * @tparam List Value lists to concatenate. */ template using value_list_cat_t = typename value_list_cat::type; /*! @brief Same as std::is_invocable, but with tuples. */ template struct is_applicable: std::false_type {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @copybrief is_applicable * @tparam Func A valid function type. * @tparam Tuple Tuple-like type. * @tparam Args The list of arguments to use to probe the function type. */ template class Tuple, typename... Args> struct is_applicable>: std::is_invocable {}; /** * @brief Helper variable template. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_v = is_applicable::value; /*! @brief Same as std::is_invocable_r, but with tuples for arguments. */ template struct is_applicable_r: std::false_type {}; /** * @copybrief is_applicable_r * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template struct is_applicable_r>: std::is_invocable_r {}; /** * @brief Helper variable template. * @tparam Ret The type to which the return type of the function should be * convertible. * @tparam Func A valid function type. * @tparam Args The list of arguments to use to probe the function type. */ template inline constexpr bool is_applicable_r_v = is_applicable_r::value; /** * @brief Provides the member constant `value` to true if a given type is * complete, false otherwise. * @tparam Type The type to test. */ template struct is_complete: std::false_type {}; /*! @copydoc is_complete */ template struct is_complete>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_complete_v = is_complete::value; /** * @brief Provides the member constant `value` to true if a given type is an * iterator, false otherwise. * @tparam Type The type to test. */ template struct is_iterator: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_iterator_category: std::false_type {}; template struct has_iterator_category::iterator_category>>: std::true_type {}; } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_iterator */ template struct is_iterator>, void>>> : internal::has_iterator_category {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_iterator_v = is_iterator::value; /** * @brief Provides the member constant `value` to true if a given type is both * an empty and non-final class, false otherwise. * @tparam Type The type to test */ template struct is_ebco_eligible : std::conjunction, std::negation>> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_ebco_eligible_v = is_ebco_eligible::value; /** * @brief Provides the member constant `value` to true if `Type::is_transparent` * is valid and denotes a type, false otherwise. * @tparam Type The type to test. */ template struct is_transparent: std::false_type {}; /*! @copydoc is_transparent */ template struct is_transparent>: std::true_type {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_transparent_v = is_transparent::value; /** * @brief Provides the member constant `value` to true if a given type is * equality comparable, false otherwise. * @tparam Type The type to test. */ template struct is_equality_comparable: std::false_type {}; /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct has_tuple_size_value: std::false_type {}; template struct has_tuple_size_value::value)>>: std::true_type {}; template [[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { return (is_equality_comparable>::value && ...); } template [[nodiscard]] constexpr bool maybe_equality_comparable(choice_t<0>) { return true; } template [[nodiscard]] constexpr auto maybe_equality_comparable(choice_t<1>) -> decltype(std::declval(), bool{}) { if constexpr(is_iterator_v) { return true; } else if constexpr(std::is_same_v) { return maybe_equality_comparable(choice<0>); } else { return is_equality_comparable::value; } } template [[nodiscard]] constexpr std::enable_if_t>>, bool> maybe_equality_comparable(choice_t<2>) { if constexpr(has_tuple_size_value::value) { return unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); } else { return maybe_equality_comparable(choice<1>); } } } // namespace internal /** * Internal details not to be documented. * @endcond */ /*! @copydoc is_equality_comparable */ template struct is_equality_comparable() == std::declval())>> : std::bool_constant(choice<2>)> {}; /** * @brief Helper variable template. * @tparam Type The type to test. */ template inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; /** * @brief Transcribes the constness of a type to another type. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = std::remove_const_t; }; /*! @copydoc constness_as */ template struct constness_as { /*! @brief The type resulting from the transcription of the constness. */ using type = const To; }; /** * @brief Alias template to facilitate the transcription of the constness. * @tparam To The type to which to transcribe the constness. * @tparam From The type from which to transcribe the constness. */ template using constness_as_t = typename constness_as::type; /** * @brief Extracts the class of a non-static member object or function. * @tparam Member A pointer to a non-static member object or function. */ template class member_class { static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); template static Class *clazz(Ret (Class::*)(Args...)); template static Class *clazz(Ret (Class::*)(Args...) const); template static Class *clazz(Type Class::*); public: /*! @brief The class of the given non-static member object or function. */ using type = std::remove_pointer_t()))>; }; /** * @brief Helper type. * @tparam Member A pointer to a non-static member object or function. */ template using member_class_t = typename member_class::type; /** * @brief Extracts the n-th argument of a given function or member function. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template class nth_argument { template static constexpr type_list pick_up(Ret (*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...)); template static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); template static constexpr type_list pick_up(Type Class ::*); public: /*! @brief N-th argument of the given function or member function. */ using type = type_list_element_t; }; /** * @brief Helper type. * @tparam Index The index of the argument to extract. * @tparam Candidate A valid function, member function or data member. */ template using nth_argument_t = typename nth_argument::type; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct compressed_pair_element { using reference = Type &; using const_reference = const Type &; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : value{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : value{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : value{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return value; } [[nodiscard]] constexpr const_reference get() const noexcept { return value; } private: Type value; }; template struct compressed_pair_element>>: Type { using reference = Type &; using const_reference = const Type &; using base_type = Type; template>> constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) : base_type{} {} template>, compressed_pair_element>>> constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(arg)} {} template constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) : base_type{std::forward(std::get(args))...} {} [[nodiscard]] constexpr reference get() noexcept { return *this; } [[nodiscard]] constexpr const_reference get() const noexcept { return *this; } }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief A compressed pair. * * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to * reduce its final size to a minimum. * * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template class compressed_pair final : internal::compressed_pair_element, internal::compressed_pair_element { using first_base = internal::compressed_pair_element; using second_base = internal::compressed_pair_element; public: /*! @brief The type of the first element that the pair stores. */ using first_type = First; /*! @brief The type of the second element that the pair stores. */ using second_type = Second; /** * @brief Default constructor, conditionally enabled. * * This constructor is only available when the types that the pair stores * are both at least default constructible. * * @tparam Dummy Dummy template parameter used for internal purposes. */ template && std::is_default_constructible_v>> constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) : first_base{}, second_base{} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; /** * @brief Move constructor. * @param other The instance to move from. */ constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; /** * @brief Constructs a pair from its values. * @tparam Arg Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. * @param arg Value to use to initialize the first element. * @param other Value to use to initialize the second element. */ template constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::forward(arg)}, second_base{std::forward(other)} {} /** * @brief Constructs a pair by forwarding the arguments to its parts. * @tparam Args Types of arguments to use to initialize the first element. * @tparam Other Types of arguments to use to initialize the second element. * @param args Arguments to use to initialize the first element. * @param other Arguments to use to initialize the second element. */ template constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) : first_base{std::move(args), std::index_sequence_for{}}, second_base{std::move(other), std::index_sequence_for{}} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; /** * @brief Move assignment operator. * @param other The instance to move from. * @return This compressed pair object. */ constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; /** * @brief Returns the first element that a pair stores. * @return The first element that a pair stores. */ [[nodiscard]] constexpr first_type &first() noexcept { return static_cast(*this).get(); } /*! @copydoc first */ [[nodiscard]] constexpr const first_type &first() const noexcept { return static_cast(*this).get(); } /** * @brief Returns the second element that a pair stores. * @return The second element that a pair stores. */ [[nodiscard]] constexpr second_type &second() noexcept { return static_cast(*this).get(); } /*! @copydoc second */ [[nodiscard]] constexpr const second_type &second() const noexcept { return static_cast(*this).get(); } /** * @brief Swaps two compressed pair objects. * @param other The compressed pair to swap with. */ constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { using std::swap; swap(first(), other.first()); swap(second(), other.second()); } /** * @brief Extracts an element from the compressed pair. * @tparam Index An integer value that is either 0 or 1. * @return Returns a reference to the first element if `Index` is 0 and a * reference to the second element if `Index` is 1. */ template constexpr decltype(auto) get() noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } /*! @copydoc get */ template constexpr decltype(auto) get() const noexcept { if constexpr(Index == 0u) { return first(); } else { static_assert(Index == 1u, "Index out of bounds"); return second(); } } }; /** * @brief Deduction guide. * @tparam Type Type of value to use to initialize the first element. * @tparam Other Type of value to use to initialize the second element. */ template compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; /** * @brief Swaps two compressed pair objects. * @tparam First The type of the first element that the pairs store. * @tparam Second The type of the second element that the pairs store. * @param lhs A valid compressed pair object. * @param rhs A valid compressed pair object. */ template inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { lhs.swap(rhs); } } // namespace entt // disable structured binding support for clang 6, it messes when specializing tuple_size #if !defined __clang_major__ || __clang_major__ > 6 namespace std { /** * @brief `std::tuple_size` specialization for `compressed_pair`s. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_size>: integral_constant {}; /** * @brief `std::tuple_element` specialization for `compressed_pair`s. * @tparam Index The index of the type to return. * @tparam First The type of the first element that the pair stores. * @tparam Second The type of the second element that the pair stores. */ template struct tuple_element>: conditional { static_assert(Index < 2u, "Index out of bounds"); }; } // namespace std #endif #endif // #include "../core/fwd.hpp" #ifndef ENTT_CORE_FWD_HPP #define ENTT_CORE_FWD_HPP #include // #include "../config/config.h" namespace entt { template class basic_any; /*! @brief Alias declaration for type identifiers. */ using id_type = ENTT_ID_TYPE; /*! @brief Alias declaration for the most common use case. */ using any = basic_any<>; } // namespace entt #endif // #include "../core/type_info.hpp" #ifndef ENTT_CORE_TYPE_INFO_HPP #define ENTT_CORE_TYPE_INFO_HPP #include #include #include // #include "../config/config.h" // #include "../core/attribute.h" #ifndef ENTT_CORE_ATTRIBUTE_H #define ENTT_CORE_ATTRIBUTE_H #ifndef ENTT_EXPORT # if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER # define ENTT_EXPORT __declspec(dllexport) # define ENTT_IMPORT __declspec(dllimport) # define ENTT_HIDDEN # elif defined __GNUC__ && __GNUC__ >= 4 # define ENTT_EXPORT __attribute__((visibility("default"))) # define ENTT_IMPORT __attribute__((visibility("default"))) # define ENTT_HIDDEN __attribute__((visibility("hidden"))) # else /* Unsupported compiler */ # define ENTT_EXPORT # define ENTT_IMPORT # define ENTT_HIDDEN # endif #endif #ifndef ENTT_API # if defined ENTT_API_EXPORT # define ENTT_API ENTT_EXPORT # elif defined ENTT_API_IMPORT # define ENTT_API ENTT_IMPORT # else /* No API */ # define ENTT_API # endif #endif #endif // #include "fwd.hpp" // #include "hashed_string.hpp" #ifndef ENTT_CORE_HASHED_STRING_HPP #define ENTT_CORE_HASHED_STRING_HPP #include #include // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template struct fnv1a_traits; template<> struct fnv1a_traits { using type = std::uint32_t; static constexpr std::uint32_t offset = 2166136261; static constexpr std::uint32_t prime = 16777619; }; template<> struct fnv1a_traits { using type = std::uint64_t; static constexpr std::uint64_t offset = 14695981039346656037ull; static constexpr std::uint64_t prime = 1099511628211ull; }; template struct basic_hashed_string { using value_type = Char; using size_type = std::size_t; using hash_type = id_type; const value_type *repr; size_type length; hash_type hash; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Zero overhead unique identifier. * * A hashed string is a compile-time tool that allows users to use * human-readable identifiers 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. * * @warning * This class doesn't take ownership of user-supplied strings nor does it make a * copy of them. * * @tparam Char Character type. */ template class basic_hashed_string: internal::basic_hashed_string { using base_type = internal::basic_hashed_string; using hs_traits = internal::fnv1a_traits; struct const_wrapper { // non-explicit constructor on purpose constexpr const_wrapper(const Char *str) noexcept : repr{str} {} const Char *repr; }; // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { base_type base{str, 0u, hs_traits::offset}; for(; str[base.length]; ++base.length) { base.hash = (base.hash ^ static_cast(str[base.length])) * hs_traits::prime; } return base; } // Fowler–Noll–Vo hash function v. 1a - the good [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { base_type base{str, len, hs_traits::offset}; for(size_type pos{}; pos < len; ++pos) { base.hash = (base.hash ^ static_cast(str[pos])) * hs_traits::prime; } return base; } public: /*! @brief Character type. */ using value_type = typename base_type::value_type; /*! @brief Unsigned integer type. */ using size_type = typename base_type::size_type; /*! @brief Unsigned integer type. */ using hash_type = typename base_type::hash_type; /** * @brief Returns directly the numeric representation of a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { return basic_hashed_string{str, len}; } /** * @brief Returns directly the numeric representation of a string. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. * @return The numeric representation of the string. */ template [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { return basic_hashed_string{str}; } /** * @brief Returns directly the numeric representation of a string. * @param wrapper Helps achieving the purpose by relying on overloading. * @return The numeric representation of the string. */ [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { return basic_hashed_string{wrapper}; } /*! @brief Constructs an empty hashed string. */ constexpr basic_hashed_string() noexcept : base_type{} {} /** * @brief Constructs a hashed string from a string view. * @param str Human-readable identifier. * @param len Length of the string to hash. */ constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept : base_type{helper(str, len)} {} /** * @brief Constructs a hashed string from an array of const characters. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template constexpr basic_hashed_string(const value_type (&str)[N]) noexcept : base_type{helper(str)} {} /** * @brief Explicit constructor on purpose to avoid constructing a hashed * string directly from a `const value_type *`. * * @warning * The lifetime of the string is not extended nor is it copied. * * @param wrapper Helps achieving the purpose by relying on overloading. */ explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept : base_type{helper(wrapper.repr)} {} /** * @brief Returns the size a hashed string. * @return The size of the hashed string. */ [[nodiscard]] constexpr size_type size() const noexcept { return base_type::length; } /** * @brief Returns the human-readable representation of a hashed string. * @return The string used to initialize the hashed string. */ [[nodiscard]] constexpr const value_type *data() const noexcept { return base_type::repr; } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr hash_type value() const noexcept { return base_type::hash; } /*! @copydoc data */ [[nodiscard]] constexpr operator const value_type *() const noexcept { return data(); } /** * @brief Returns the numeric representation of a hashed string. * @return The numeric representation of the hashed string. */ [[nodiscard]] constexpr operator hash_type() const noexcept { return value(); } }; /** * @brief Deduction guide. * @tparam Char Character type. * @param str Human-readable identifier. * @param len Length of the string to hash. */ template basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; /** * @brief Deduction guide. * @tparam Char Character type. * @tparam N Number of characters of the identifier. * @param str Human-readable identifier. */ template basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; /** * @brief Compares two hashed strings. * @tparam Char Character type. * @param lhs A valid hashed string. * @param rhs A valid hashed string. * @return True if the two hashed strings are identical, false otherwise. */ template [[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() == rhs.value(); } /** * @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 differ, false otherwise. */ template [[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs == rhs); } /** * @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 first element is less than the second, false otherwise. */ template [[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return lhs.value() < rhs.value(); } /** * @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 first element is less than or equal to the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(rhs < lhs); } /** * @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 first element is greater than the second, false * otherwise. */ template [[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return rhs < lhs; } /** * @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 first element is greater than or equal to the second, * false otherwise. */ template [[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { return !(lhs < rhs); } /*! @brief Aliases for common character types. */ using hashed_string = basic_hashed_string; /*! @brief Aliases for common character types. */ using hashed_wstring = basic_hashed_string; inline namespace literals { /** * @brief User defined literal for hashed strings. * @param str The literal without its suffix. * @return A properly initialized hashed string. */ [[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { return hashed_string{str}; } /** * @brief User defined literal for hashed wstrings. * @param str The literal without its suffix. * @return A properly initialized hashed wstring. */ [[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { return hashed_wstring{str}; } } // namespace literals } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct ENTT_API type_index final { [[nodiscard]] static id_type next() noexcept { static ENTT_MAYBE_ATOMIC(id_type) value{}; return value++; } }; template [[nodiscard]] constexpr auto stripped_type_name() noexcept { #if defined ENTT_PRETTY_FUNCTION std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); return value; #else return std::string_view{""}; #endif } template().find_first_of('.')> [[nodiscard]] static constexpr std::string_view type_name(int) noexcept { constexpr auto value = stripped_type_name(); return value; } template [[nodiscard]] static std::string_view type_name(char) noexcept { static const auto value = stripped_type_name(); return value; } template().find_first_of('.')> [[nodiscard]] static constexpr id_type type_hash(int) noexcept { constexpr auto stripped = stripped_type_name(); constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); return value; } template [[nodiscard]] static id_type type_hash(char) noexcept { static const auto value = [](const auto stripped) { return hashed_string::value(stripped.data(), stripped.size()); }(stripped_type_name()); return value; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Type sequential identifier. * @tparam Type Type for which to generate a sequential identifier. */ template struct ENTT_API type_index final { /** * @brief Returns the sequential identifier of a given type. * @return The sequential identifier of a given type. */ [[nodiscard]] static id_type value() noexcept { static const id_type value = internal::type_index::next(); return value; } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type hash. * @tparam Type Type for which to generate a hash value. */ template struct type_hash final { /** * @brief Returns the numeric representation of a given type. * @return The numeric representation of the given type. */ #if defined ENTT_PRETTY_FUNCTION [[nodiscard]] static constexpr id_type value() noexcept { return internal::type_hash(0); #else [[nodiscard]] static constexpr id_type value() noexcept { return type_index::value(); #endif } /*! @copydoc value */ [[nodiscard]] constexpr operator id_type() const noexcept { return value(); } }; /** * @brief Type name. * @tparam Type Type for which to generate a name. */ template struct type_name final { /** * @brief Returns the name of a given type. * @return The name of the given type. */ [[nodiscard]] static constexpr std::string_view value() noexcept { return internal::type_name(0); } /*! @copydoc value */ [[nodiscard]] constexpr operator std::string_view() const noexcept { return value(); } }; /*! @brief Implementation specific information about a type. */ struct type_info final { /** * @brief Constructs a type info object for a given type. * @tparam Type Type for which to construct a type info object. */ template constexpr type_info(std::in_place_type_t) noexcept : seq{type_index>>::value()}, identifier{type_hash>>::value()}, alias{type_name>>::value()} {} /** * @brief Type index. * @return Type index. */ [[nodiscard]] constexpr id_type index() const noexcept { return seq; } /** * @brief Type hash. * @return Type hash. */ [[nodiscard]] constexpr id_type hash() const noexcept { return identifier; } /** * @brief Type name. * @return Type name. */ [[nodiscard]] constexpr std::string_view name() const noexcept { return alias; } private: id_type seq; id_type identifier; std::string_view alias; }; /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects are identical, false otherwise. */ [[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { return lhs.hash() == rhs.hash(); } /** * @brief Compares the contents of two type info objects. * @param lhs A type info object. * @param rhs A type info object. * @return True if the two type info objects differ, false otherwise. */ [[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs == rhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than the second, false otherwise. */ [[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { return lhs.index() < rhs.index(); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is less than or equal to the second, false * otherwise. */ [[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { return !(rhs < lhs); } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than the second, false * otherwise. */ [[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { return rhs < lhs; } /** * @brief Compares two type info objects. * @param lhs A valid type info object. * @param rhs A valid type info object. * @return True if the first element is greater than or equal to the second, * false otherwise. */ [[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { return !(lhs < rhs); } /** * @brief Returns the type info object associated to a given type. * * The returned element refers to an object with static storage duration.
* The type doesn't need to be a complete type. If the type is a reference, the * result refers to the referenced type. In all cases, top-level cv-qualifiers * are ignored. * * @tparam Type Type for which to generate a type info object. * @return A reference to a properly initialized type info object. */ template [[nodiscard]] const type_info &type_id() noexcept { if constexpr(std::is_same_v>>) { static type_info instance{std::in_place_type}; return instance; } else { return type_id>>(); } } /*! @copydoc type_id */ template [[nodiscard]] const type_info &type_id(Type &&) noexcept { return type_id>>(); } } // namespace entt #endif // #include "../core/utility.hpp" #ifndef ENTT_CORE_UTILITY_HPP #define ENTT_CORE_UTILITY_HPP #include #include namespace entt { /*! @brief Identity function object (waiting for C++20). */ struct identity { /*! @brief Indicates that this is a transparent function object. */ using is_transparent = void; /** * @brief Returns its argument unchanged. * @tparam Type Type of the argument. * @param value The actual argument. * @return The submitted value as-is. */ template [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { return std::forward(value); } }; /** * @brief Constant utility to disambiguate overloaded members of a class. * @tparam Type Type of the desired overload. * @tparam Class Type of class to which the member belongs. * @param member A valid pointer to a member. * @return Pointer to the member. */ template [[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { return member; } /** * @brief Constant utility to disambiguate overloaded functions. * @tparam Func Function type of the desired overload. * @param func A valid pointer to a function. * @return Pointer to the function. */ template [[nodiscard]] constexpr auto overload(Func *func) noexcept { return func; } /** * @brief Helper type for visitors. * @tparam Func Types of function objects. */ template struct overloaded: Func... { using Func::operator()...; }; /** * @brief Deduction guide. * @tparam Func Types of function objects. */ template overloaded(Func...) -> overloaded; /** * @brief Basic implementation of a y-combinator. * @tparam Func Type of a potentially recursive function. */ template struct y_combinator { /** * @brief Constructs a y-combinator from a given function. * @param recursive A potentially recursive function. */ constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) : 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 constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } /*! @copydoc operator()() */ template constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { return func(*this, std::forward(args)...); } private: Func func; }; } // namespace entt #endif // #include "fwd.hpp" // #include "sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include // #include "delegate.hpp" #ifndef ENTT_SIGNAL_DELEGATE_HPP #define ENTT_SIGNAL_DELEGATE_HPP #include #include #include #include #include // #include "../config/config.h" // #include "../core/type_traits.hpp" // #include "fwd.hpp" namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { template constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); template constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); template using function_pointer_t = decltype(function_pointer(std::declval()...)); template [[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { return std::index_sequence_for{}; } } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic delegate implementation. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. */ template class delegate; /** * @brief Utility class to use to send around functions and members. * * Unmanaged delegate for function pointers and members. Users of this class are * in charge of disconnecting instances before deleting them. * * A delegate can be used as a general purpose invoker without memory overhead * for free functions possibly with payloads and bound or unbound members. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template class delegate { template [[nodiscard]] auto wrap(std::index_sequence) noexcept { return [](const void *, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); }; } template [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { return [](const void *payload, Args... args) -> Ret { [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); Type *curr = static_cast(const_cast *>(payload)); return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); }; } public: /*! @brief Function type of the contained target. */ using function_type = Ret(const void *, Args...); /*! @brief Function type of the delegate. */ using type = Ret(Args...); /*! @brief Return type of the delegate. */ using result_type = Ret; /*! @brief Default constructor. */ delegate() noexcept : instance{nullptr}, fn{nullptr} {} /** * @brief Constructs a delegate with a given object or payload, if any. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance Optional valid object that fits the purpose. */ template delegate(connect_arg_t, Type &&...value_or_instance) noexcept { connect(std::forward(value_or_instance)...); } /** * @brief Constructs a delegate and connects an user defined function with * optional payload. * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ delegate(function_type *function, const void *payload = nullptr) noexcept { connect(function, payload); } /** * @brief Connects a free function or an unbound member to a delegate. * @tparam Candidate Function or member to connect to the delegate. */ template void connect() noexcept { instance = nullptr; if constexpr(std::is_invocable_r_v) { fn = [](const void *, Args... args) -> Ret { return Ret(std::invoke(Candidate, std::forward(args)...)); }; } else if constexpr(std::is_member_pointer_v) { fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); } else { fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of the instance overcomes * the one of the delegate.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the delegate itself. * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid reference that fits the purpose. */ template void connect(Type &value_or_instance) noexcept { instance = &value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects a free function with payload or a bound member to a * delegate. * * @sa connect(Type &) * * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid pointer that fits the purpose. */ template void connect(Type *value_or_instance) noexcept { instance = value_or_instance; if constexpr(std::is_invocable_r_v) { fn = [](const void *payload, Args... args) -> Ret { Type *curr = static_cast(const_cast *>(payload)); return Ret(std::invoke(Candidate, curr, std::forward(args)...)); }; } else { fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); } } /** * @brief Connects an user defined function with optional payload to a * delegate. * * The delegate isn't responsible for the connected object or the payload. * Users must always guarantee that the lifetime of an instance overcomes * the one of the delegate.
* The payload is returned as the first argument to the target function in * all cases. * * @param function Function to connect to the delegate. * @param payload User defined arbitrary data. */ void connect(function_type *function, const void *payload = nullptr) noexcept { ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); instance = payload; fn = function; } /** * @brief Resets a delegate. * * After a reset, a delegate cannot be invoked anymore. */ void reset() noexcept { instance = nullptr; fn = nullptr; } /** * @brief Returns the instance or the payload linked to a delegate, if any. * @return An opaque pointer to the underlying data. */ [[nodiscard]] const void *data() const noexcept { return instance; } /** * @brief Triggers a delegate. * * The delegate invokes the underlying function and returns the result. * * @warning * Attempting to trigger an invalid delegate results in undefined * behavior. * * @param args Arguments to use to invoke the underlying function. * @return The value returned by the underlying function. */ Ret operator()(Args... args) const { ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); return fn(instance, std::forward(args)...); } /** * @brief Checks whether a delegate actually stores a listener. * @return False if the delegate is empty, true otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { // no need to also test instance return !(fn == nullptr); } /** * @brief Compares the contents of two delegates. * @param other Delegate with which to compare. * @return False if the two contents differ, true otherwise. */ [[nodiscard]] bool operator==(const delegate &other) const noexcept { return fn == other.fn && instance == other.instance; } private: const void *instance; function_type *fn; }; /** * @brief Compares the contents of two delegates. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @param lhs A valid delegate object. * @param rhs A valid delegate object. * @return True if the two contents differ, false otherwise. */ template [[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { return !(lhs == rhs); } /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. */ template delegate(connect_arg_t) -> delegate>>; /** * @brief Deduction guide. * @tparam Candidate Function or member to connect to the delegate. * @tparam Type Type of class or type of payload. */ template delegate(connect_arg_t, Type &&) -> delegate>>; /** * @brief Deduction guide. * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. */ template delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; } // namespace entt #endif // #include "fwd.hpp" namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid signal handler type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink>; using alloc_traits = std::allocator_traits; using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink>; /*! @brief Default constructor. */ sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{allocator} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This signal handler. */ sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This signal handler. */ sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } /** * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const noexcept { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for(auto &&call: std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for(auto &&call: calls) { if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); if(func()) { break; } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { if(func(call(args...))) { break; } } else { func(call(args...)); } } } } private: container_type calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void *ref) : disconnect{fn}, signal{ref} {} public: /*! @brief Default constructor. */ connection() : disconnect{}, signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if(disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void *signal; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection &other) : conn{other} {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection &) = delete; /** * @brief Move constructor. * @param other The scoped connection to move from. */ scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection &operator=(const scoped_connection &) = delete; /** * @brief Move assignment operator. * @param other The scoped connection to move from. * @return This scoped connection. */ scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection &operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sink> { using signal_type = sigh; using difference_type = typename signal_type::container_type::difference_type; template static void release(Type value_or_instance, void *signal) { sink{*static_cast(signal)}.disconnect(value_or_instance); } template static void release(void *signal) { sink{*static_cast(signal)}.disconnect(); } auto before(delegate call) { const auto &calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{*this}; other.offset = calls.cend() - it; return other; } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh &ref) noexcept : offset{}, signal{&ref} {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); return before(std::move(call)); } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); return before(std::move(call)); } /** * @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>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ [[nodiscard]] sink before(const void *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.data() == value_or_instance; }); other.offset = calls.cend() - it; } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{*this}; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function (with or without payload), a bound or an * unbound member to a signal. * * The signal isn't responsible for the connected object or the payload, if * any. Users must guarantee that the lifetime of the instance overcomes the * one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. * @return A properly initialized connection object. */ template connection connect(Type &&...value_or_instance) { disconnect(value_or_instance...); delegate call{}; call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** * @brief Disconnects a free function (with or without payload), a bound or * an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. */ template void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @param value_or_instance A valid object that fits the purpose. */ void disconnect(const void *value_or_instance) { if(value_or_instance) { auto &calls = signal->calls; auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type *signal; }; /** * @brief Deduction guide. * * It allows to deduce the signal handler type of a sink directly from the * signal it refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template sink(sigh &) -> sink>; } // namespace entt #endif namespace entt { /** * @cond TURN_OFF_DOXYGEN * Internal details not to be documented. */ namespace internal { struct basic_dispatcher_handler { virtual ~basic_dispatcher_handler() = default; virtual void publish() = 0; virtual void disconnect(void *) = 0; virtual void clear() noexcept = 0; virtual std::size_t size() const noexcept = 0; }; template class dispatcher_handler final: public basic_dispatcher_handler { static_assert(std::is_same_v>, "Invalid type"); using alloc_traits = std::allocator_traits; using signal_type = sigh; using container_type = std::vector>; public: using allocator_type = Allocator; dispatcher_handler(const allocator_type &allocator) : signal{allocator}, events{allocator} {} void publish() override { const auto length = events.size(); for(std::size_t pos{}; pos < length; ++pos) { signal.publish(events[pos]); } events.erase(events.cbegin(), events.cbegin() + length); } void disconnect(void *instance) override { bucket().disconnect(instance); } void clear() noexcept override { events.clear(); } [[nodiscard]] auto bucket() noexcept { return typename signal_type::sink_type{signal}; } void trigger(Type event) { signal.publish(event); } template void enqueue(Args &&...args) { if constexpr(std::is_aggregate_v) { events.push_back(Type{std::forward(args)...}); } else { events.emplace_back(std::forward(args)...); } } std::size_t size() const noexcept override { return events.size(); } private: signal_type signal; container_type events; }; } // namespace internal /** * Internal details not to be documented. * @endcond */ /** * @brief Basic dispatcher implementation. * * A dispatcher can be used either to trigger an immediate event or to enqueue * events to be published all together once per tick.
* Listeners are provided in the form of member functions. For each event of * type `Type`, listeners are such that they can be invoked with an argument of * type `Type &`, no matter what the return type is. * * The dispatcher creates instances of the `sigh` class internally. Refer to the * documentation of the latter for more details. * * @tparam Allocator Type of allocator used to manage memory and elements. */ template class basic_dispatcher { template using handler_type = internal::dispatcher_handler; using key_type = id_type; // std::shared_ptr because of its type erased allocator which is useful here using mapped_type = std::shared_ptr; using alloc_traits = std::allocator_traits; using container_allocator = typename alloc_traits::template rebind_alloc>; using container_type = dense_map, container_allocator>; template [[nodiscard]] handler_type &assure(const id_type id) { static_assert(std::is_same_v>, "Non-decayed types not allowed"); auto &&ptr = pools.first()[id]; if(!ptr) { const auto &allocator = get_allocator(); ptr = std::allocate_shared>(allocator, allocator); } return static_cast &>(*ptr); } template [[nodiscard]] const handler_type *assure(const id_type id) const { static_assert(std::is_same_v>, "Non-decayed types not allowed"); if(auto it = pools.first().find(id); it != pools.first().cend()) { return static_cast *>(it->second.get()); } return nullptr; } public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Default constructor. */ basic_dispatcher() : basic_dispatcher{allocator_type{}} {} /** * @brief Constructs a dispatcher with a given allocator. * @param allocator The allocator to use. */ explicit basic_dispatcher(const allocator_type &allocator) : pools{allocator, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ basic_dispatcher(basic_dispatcher &&other) noexcept : pools{std::move(other.pools)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept : pools{container_type{std::move(other.pools.first()), allocator}, allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This dispatcher. */ basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { pools = std::move(other.pools); return *this; } /** * @brief Exchanges the contents with those of a given dispatcher. * @param other Dispatcher to exchange the content with. */ void swap(basic_dispatcher &other) { using std::swap; swap(pools, other.pools); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return pools.second(); } /** * @brief Returns the number of pending events for a given type. * @tparam Type Type of event for which to return the count. * @param id Name used to map the event queue within the dispatcher. * @return The number of pending events for the given type. */ template size_type size(const id_type id = type_hash::value()) const noexcept { const auto *cpool = assure>(id); return cpool ? cpool->size() : 0u; } /** * @brief Returns the total number of pending events. * @return The total number of pending events. */ size_type size() const noexcept { size_type count{}; for(auto &&cpool: pools.first()) { count += cpool.second->size(); } return count; } /** * @brief Returns a sink object for the given event and queue. * * A sink is an opaque object used to connect listeners to events. * * The function type for a listener is _compatible_ with: * * @code{.cpp} * void(Type &); * @endcode * * The order of invocation of the listeners isn't guaranteed. * * @sa sink * * @tparam Type Type of event of which to get the sink. * @param id Name used to map the event queue within the dispatcher. * @return A temporary sink object. */ template [[nodiscard]] auto sink(const id_type id = type_hash::value()) { return assure(id).bucket(); } /** * @brief Triggers an immediate event of a given type. * @tparam Type Type of event to trigger. * @param value An instance of the given type of event. */ template void trigger(Type &&value = {}) { trigger(type_hash>::value(), std::forward(value)); } /** * @brief Triggers an immediate event on a queue of a given type. * @tparam Type Type of event to trigger. * @param value An instance of the given type of event. * @param id Name used to map the event queue within the dispatcher. */ template void trigger(const id_type id, Type &&value = {}) { assure>(id).trigger(std::forward(value)); } /** * @brief Enqueues an event of the given type. * @tparam Type Type of event to enqueue. * @tparam Args Types of arguments to use to construct the event. * @param args Arguments to use to construct the event. */ template void enqueue(Args &&...args) { enqueue_hint(type_hash::value(), std::forward(args)...); } /** * @brief Enqueues an event of the given type. * @tparam Type Type of event to enqueue. * @param value An instance of the given type of event. */ template void enqueue(Type &&value) { enqueue_hint(type_hash>::value(), std::forward(value)); } /** * @brief Enqueues an event of the given type. * @tparam Type Type of event to enqueue. * @tparam Args Types of arguments to use to construct the event. * @param id Name used to map the event queue within the dispatcher. * @param args Arguments to use to construct the event. */ template void enqueue_hint(const id_type id, Args &&...args) { assure(id).enqueue(std::forward(args)...); } /** * @brief Enqueues an event of the given type. * @tparam Type Type of event to enqueue. * @param id Name used to map the event queue within the dispatcher. * @param value An instance of the given type of event. */ template void enqueue_hint(const id_type id, Type &&value) { assure>(id).enqueue(std::forward(value)); } /** * @brief Utility function to disconnect everything related to a given value * or instance from a dispatcher. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } /** * @brief Utility function to disconnect everything related to a given value * or instance from a dispatcher. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template void disconnect(Type *value_or_instance) { for(auto &&cpool: pools.first()) { cpool.second->disconnect(value_or_instance); } } /** * @brief Discards all the events stored so far in a given queue. * @tparam Type Type of event to discard. * @param id Name used to map the event queue within the dispatcher. */ template void clear(const id_type id = type_hash::value()) { assure(id).clear(); } /*! @brief Discards all the events queued so far. */ void clear() noexcept { for(auto &&cpool: pools.first()) { cpool.second->clear(); } } /** * @brief Delivers all the pending events of a given queue. * @tparam Type Type of event to send. * @param id Name used to map the event queue within the dispatcher. */ template void update(const id_type id = type_hash::value()) { assure(id).publish(); } /*! @brief Delivers all the pending events. */ void update() const { for(auto &&cpool: pools.first()) { cpool.second->publish(); } } private: compressed_pair pools; }; } // namespace entt #endif // #include "signal/emitter.hpp" #ifndef ENTT_SIGNAL_EMITTER_HPP #define ENTT_SIGNAL_EMITTER_HPP #include #include #include // #include "../container/dense_map.hpp" // #include "../core/compressed_pair.hpp" // #include "../core/fwd.hpp" // #include "../core/type_info.hpp" // #include "../core/utility.hpp" // #include "fwd.hpp" namespace entt { /** * @brief General purpose event emitter. * * To create an emitter type, derived classes must inherit from the base as: * * @code{.cpp} * struct my_emitter: emitter { * // ... * } * @endcode * * Handlers for the different events are created internally on the fly. It's not * required to specify in advance the full list of accepted events.
* Moreover, whenever an event is published, an emitter also passes a reference * to itself to its listeners. * * @tparam Derived Emitter type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class emitter { using key_type = id_type; using mapped_type = std::function; using alloc_traits = std::allocator_traits; using container_allocator = typename alloc_traits::template rebind_alloc>; using container_type = dense_map, container_allocator>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Default constructor. */ emitter() : emitter{allocator_type{}} {} /** * @brief Constructs an emitter with a given allocator. * @param allocator The allocator to use. */ explicit emitter(const allocator_type &allocator) : handlers{allocator, allocator} {} /*! @brief Default destructor. */ virtual ~emitter() noexcept { static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); } /** * @brief Move constructor. * @param other The instance to move from. */ emitter(emitter &&other) noexcept : handlers{std::move(other.handlers)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ emitter(emitter &&other, const allocator_type &allocator) noexcept : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} {} /** * @brief Move assignment operator. * @param other The instance to move from. * @return This dispatcher. */ emitter &operator=(emitter &&other) noexcept { handlers = std::move(other.handlers); return *this; } /** * @brief Exchanges the contents with those of a given emitter. * @param other Emitter to exchange the content with. */ void swap(emitter &other) { using std::swap; swap(handlers, other.handlers); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return handlers.second(); } /** * @brief Publishes a given event. * @tparam Type Type of event to trigger. * @param value An instance of the given type of event. */ template void publish(Type &&value) { if(const auto id = type_id().hash(); handlers.first().contains(id)) { handlers.first()[id](&value); } } /** * @brief Registers a listener with the event emitter. * @tparam Type Type of event to which to connect the listener. * @param func The listener to register. */ template void on(std::function func) { handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { func(*static_cast(value), static_cast(*this)); }); } /** * @brief Disconnects a listener from the event emitter. * @tparam Type Type of event of the listener. */ template void erase() { handlers.first().erase(type_hash>>::value()); } /*! @brief Disconnects all the listeners. */ void clear() noexcept { handlers.first().clear(); } /** * @brief Checks if there are listeners registered for the specific event. * @tparam Type Type of event to test. * @return True if there are no listeners registered, false otherwise. */ template [[nodiscard]] bool contains() const { return handlers.first().contains(type_hash>>::value()); } /** * @brief Checks if there are listeners registered with the event emitter. * @return True if there are no listeners registered, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return handlers.first().empty(); } private: compressed_pair handlers; }; } // namespace entt #endif // #include "signal/sigh.hpp" #ifndef ENTT_SIGNAL_SIGH_HPP #define ENTT_SIGNAL_SIGH_HPP #include #include #include #include #include // #include "delegate.hpp" // #include "fwd.hpp" namespace entt { /** * @brief Sink class. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid signal handler type. */ template class sink; /** * @brief Unmanaged signal handler. * * Primary template isn't defined on purpose. All the specializations give a * compile-time error unless the template parameter is a function type. * * @tparam Type A valid function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh; /** * @brief Unmanaged signal handler. * * It works directly with references to classes and pointers to member functions * as well as pointers to free functions. Users of this class are in charge of * disconnecting instances before deleting them. * * This class serves mainly two purposes: * * * Creating signals to use later to notify a bunch of listeners. * * Collecting results from a set of functions like in a voting system. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sigh { /*! @brief A sink is allowed to modify a signal. */ friend class sink>; using alloc_traits = std::allocator_traits; using container_type = std::vector, typename alloc_traits::template rebind_alloc>>; public: /*! @brief Allocator type. */ using allocator_type = Allocator; /*! @brief Unsigned integer type. */ using size_type = std::size_t; /*! @brief Sink type. */ using sink_type = sink>; /*! @brief Default constructor. */ sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) : sigh{allocator_type{}} {} /** * @brief Constructs a signal handler with a given allocator. * @param allocator The allocator to use. */ explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{allocator} {} /** * @brief Copy constructor. * @param other The instance to copy from. */ sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) : calls{other.calls} {} /** * @brief Allocator-extended copy constructor. * @param other The instance to copy from. * @param allocator The allocator to use. */ sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{other.calls, allocator} {} /** * @brief Move constructor. * @param other The instance to move from. */ sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) : calls{std::move(other.calls)} {} /** * @brief Allocator-extended move constructor. * @param other The instance to move from. * @param allocator The allocator to use. */ sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) : calls{std::move(other.calls), allocator} {} /** * @brief Copy assignment operator. * @param other The instance to copy from. * @return This signal handler. */ sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { calls = other.calls; return *this; } /** * @brief Move assignment operator. * @param other The instance to move from. * @return This signal handler. */ sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { calls = std::move(other.calls); return *this; } /** * @brief Exchanges the contents with those of a given signal handler. * @param other Signal handler to exchange the content with. */ void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { using std::swap; swap(calls, other.calls); } /** * @brief Returns the associated allocator. * @return The associated allocator. */ [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { return calls.get_allocator(); } /** * @brief Number of listeners connected to the signal. * @return Number of listeners currently connected. */ [[nodiscard]] size_type size() const noexcept { return calls.size(); } /** * @brief Returns false if at least a listener is connected to the signal. * @return True if the signal has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return calls.empty(); } /** * @brief Triggers a signal. * * All the listeners are notified. Order isn't guaranteed. * * @param args Arguments to use to invoke listeners. */ void publish(Args... args) const { for(auto &&call: std::as_const(calls)) { call(args...); } } /** * @brief Collects return values from the listeners. * * The collector must expose a call operator with the following properties: * * * The return type is either `void` or such that it's convertible to * `bool`. In the second case, a true value will stop the iteration. * * The list of parameters is empty if `Ret` is `void`, otherwise it * contains a single element such that `Ret` is convertible to it. * * @tparam Func Type of collector to use, if any. * @param func A valid function object. * @param args Arguments to use to invoke listeners. */ template void collect(Func func, Args... args) const { for(auto &&call: calls) { if constexpr(std::is_void_v) { if constexpr(std::is_invocable_r_v) { call(args...); if(func()) { break; } } else { call(args...); func(); } } else { if constexpr(std::is_invocable_r_v) { if(func(call(args...))) { break; } } else { func(call(args...)); } } } } private: container_type calls; }; /** * @brief Connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it. */ class connection { /*! @brief A sink is allowed to create connection objects. */ template friend class sink; connection(delegate fn, void *ref) : disconnect{fn}, signal{ref} {} public: /*! @brief Default constructor. */ connection() : disconnect{}, signal{} {} /** * @brief Checks whether a connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(disconnect); } /*! @brief Breaks the connection. */ void release() { if(disconnect) { disconnect(signal); disconnect.reset(); } } private: delegate disconnect; void *signal; }; /** * @brief Scoped connection class. * * Opaque object the aim of which is to allow users to release an already * estabilished connection without having to keep a reference to the signal or * the sink that generated it.
* A scoped connection automatically breaks the link between the two objects * when it goes out of scope. */ struct scoped_connection { /*! @brief Default constructor. */ scoped_connection() = default; /** * @brief Constructs a scoped connection from a basic connection. * @param other A valid connection object. */ scoped_connection(const connection &other) : conn{other} {} /*! @brief Default copy constructor, deleted on purpose. */ scoped_connection(const scoped_connection &) = delete; /** * @brief Move constructor. * @param other The scoped connection to move from. */ scoped_connection(scoped_connection &&other) noexcept : conn{std::exchange(other.conn, {})} {} /*! @brief Automatically breaks the link on destruction. */ ~scoped_connection() { conn.release(); } /** * @brief Default copy assignment operator, deleted on purpose. * @return This scoped connection. */ scoped_connection &operator=(const scoped_connection &) = delete; /** * @brief Move assignment operator. * @param other The scoped connection to move from. * @return This scoped connection. */ scoped_connection &operator=(scoped_connection &&other) noexcept { conn = std::exchange(other.conn, {}); return *this; } /** * @brief Acquires a connection. * @param other The connection object to acquire. * @return This scoped connection. */ scoped_connection &operator=(connection other) { conn = std::move(other); return *this; } /** * @brief Checks whether a scoped connection is properly initialized. * @return True if the connection is properly initialized, false otherwise. */ [[nodiscard]] explicit operator bool() const noexcept { return static_cast(conn); } /*! @brief Breaks the connection. */ void release() { conn.release(); } private: connection conn; }; /** * @brief Sink class. * * A sink is used to connect listeners to signals and to disconnect them.
* The function type for a listener is the one of the signal to which it * belongs. * * The clear separation between a signal and a sink permits to store the former * as private data member without exposing the publish functionality to the * users of the class. * * @warning * Lifetime of a sink must not overcome that of the signal to which it refers. * In any other case, attempting to use a sink results in undefined behavior. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template class sink> { using signal_type = sigh; using difference_type = typename signal_type::container_type::difference_type; template static void release(Type value_or_instance, void *signal) { sink{*static_cast(signal)}.disconnect(value_or_instance); } template static void release(void *signal) { sink{*static_cast(signal)}.disconnect(); } auto before(delegate call) { const auto &calls = signal->calls; const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); sink other{*this}; other.offset = calls.cend() - it; return other; } public: /** * @brief Constructs a sink that is allowed to modify a given signal. * @param ref A valid reference to a signal object. */ sink(sigh &ref) noexcept : offset{}, signal{&ref} {} /** * @brief Returns false if at least a listener is connected to the sink. * @return True if the sink has no listeners connected, false otherwise. */ [[nodiscard]] bool empty() const noexcept { return signal->calls.empty(); } /** * @brief Returns a sink that connects before a given free function or an * unbound member. * @tparam Function A valid free function pointer. * @return A properly initialized sink object. */ template [[nodiscard]] sink before() { delegate call{}; call.template connect(); return before(std::move(call)); } /** * @brief Returns a sink that connects before a free function with payload * or a bound member. * @tparam Candidate Member or free function to look for. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. * @return A properly initialized sink object. */ template [[nodiscard]] sink before(Type &&value_or_instance) { delegate call{}; call.template connect(value_or_instance); return before(std::move(call)); } /** * @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>, void>, sink>> [[nodiscard]] sink before(Type &value_or_instance) { return before(&value_or_instance); } /** * @brief Returns a sink that connects before a given instance or specific * payload. * @param value_or_instance A valid pointer that fits the purpose. * @return A properly initialized sink object. */ [[nodiscard]] sink before(const void *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.data() == value_or_instance; }); other.offset = calls.cend() - it; } return other; } /** * @brief Returns a sink that connects before anything else. * @return A properly initialized sink object. */ [[nodiscard]] sink before() { sink other{*this}; other.offset = signal->calls.size(); return other; } /** * @brief Connects a free function (with or without payload), a bound or an * unbound member to a signal. * * The signal isn't responsible for the connected object or the payload, if * any. Users must guarantee that the lifetime of the instance overcomes the * one of the signal. On the other side, the signal handler performs * checks to avoid multiple connections for the same function.
* When used to connect a free function with payload, its signature must be * such that the instance is the first argument before the ones used to * define the signal itself. * * @tparam Candidate Function or member to connect to the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. * @return A properly initialized connection object. */ template connection connect(Type &&...value_or_instance) { disconnect(value_or_instance...); delegate call{}; call.template connect(value_or_instance...); signal->calls.insert(signal->calls.end() - offset, std::move(call)); delegate conn{}; conn.template connect<&release>(value_or_instance...); return {std::move(conn), signal}; } /** * @brief Disconnects a free function (with or without payload), a bound or * an unbound member from a signal. * @tparam Candidate Function or member to disconnect from the signal. * @tparam Type Type of class or type of payload, if any. * @param value_or_instance A valid object that fits the purpose, if any. */ template void disconnect(Type &&...value_or_instance) { auto &calls = signal->calls; delegate call{}; call.template connect(value_or_instance...); calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @tparam Type Type of class or type of payload. * @param value_or_instance A valid object that fits the purpose. */ template>, void>>> void disconnect(Type &value_or_instance) { disconnect(&value_or_instance); } /** * @brief Disconnects free functions with payload or bound members from a * signal. * @param value_or_instance A valid object that fits the purpose. */ void disconnect(const void *value_or_instance) { if(value_or_instance) { auto &calls = signal->calls; auto predicate = [value_or_instance](const auto &delegate) { return delegate.data() == value_or_instance; }; calls.erase(std::remove_if(calls.begin(), calls.end(), std::move(predicate)), calls.end()); } } /*! @brief Disconnects all the listeners from a signal. */ void disconnect() { signal->calls.clear(); } private: difference_type offset; signal_type *signal; }; /** * @brief Deduction guide. * * It allows to deduce the signal handler type of a sink directly from the * signal it refers to. * * @tparam Ret Return type of a function type. * @tparam Args Types of arguments of a function type. * @tparam Allocator Type of allocator used to manage memory and elements. */ template sink(sigh &) -> sink>; } // namespace entt #endif // IWYU pragma: end_exports