diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c82274..229d1c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -69,6 +69,7 @@ set(LUNARIUM_SRC "src/utils/high_resolution_timer.cpp" "src/utils/logger.cpp" "src/utils/op_res.cpp" +"src/utils/uuid.cpp" "src/platform/window.cpp" "src/platform/terminal.cpp" "src/graphics/opengl/glGraphics.cpp" @@ -87,6 +88,7 @@ set(LUNARIUM_SRC "src/scripting/script_manager.cpp" "src/scripting/coreAPI.cpp" "src/world/world.cpp" +"src/world/entity.cpp" ) # add the executable @@ -175,6 +177,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC external/glad/include PUBLIC external/freetype/include PUBLIC external/box2d/include + PUBLIC external/entt PUBLIC external/nativefiledialog-extended/src/include ) diff --git a/external/entt/entt.hpp b/external/entt/entt.hpp new file mode 100644 index 0000000..efec35b --- /dev/null +++ b/external/entt/entt.hpp @@ -0,0 +1,66130 @@ +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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 +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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; + + dense_map_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_map_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_map_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_map_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + dense_map_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_map_iterator operator--(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_map_iterator copy = *this; + return (copy += value); + } + + dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->element.first, it->element.second}; + } + + template + friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_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; + + dense_map_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].next, *this; + } + + dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const allocator_type &allocator) + : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_map{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 + * 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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 +// #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; + + dense_set_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_set_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_set_iterator(const dense_set_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_set_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_set_iterator operator++(int) ENTT_NOEXCEPT { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + dense_set_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_set_iterator operator--(int) ENTT_NOEXCEPT { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_set_iterator copy = *this; + return (copy += value); + } + + dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return it[value].second; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return std::addressof(it->second); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + template + friend std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_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; + + dense_set_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_set_local_iterator(const dense_set_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_set_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].first, *this; + } + + dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return std::addressof(it[offset].second); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type bucket_count, const allocator_type &allocator) + : dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_set{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#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 "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + enum class operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get + }; + + enum class policy : std::uint8_t { + owner, + ref, + cref + }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] 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 *static_cast(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) { + if constexpr(!std::is_void_v) { + info = &type_id>>(); + 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) Type{std::forward(args)...}; + } else { + new(&storage) Type(std::forward(args)...); + } + } else { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + : instance{other.data()}, + 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() ENTT_NOEXCEPT + : instance{}, + info{&type_id()}, + vtable{}, + mode{policy::owner} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : basic_any{} { + 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{} { + initialize>(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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + } + + /** + * @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) ENTT_NOEXCEPT { + return *info == req ? data() : nullptr; + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + 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 any &other) { + if(vtable && mode != policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(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); + } + + 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 ENTT_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. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + return basic_any{*this, policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return (mode == policy::owner); + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + 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) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +Type *any_cast(basic_any *data) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + // last attempt to make wrappers for const references return their values + return static_cast(static_cast, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +} // 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 "../config/config.h" + +// #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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 +// #include "../config/config.h" + + +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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) ENTT_NOEXCEPT { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) ENTT_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 family_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const family_type type = identifier++; +}; + +} // namespace entt + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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 "../config/config.h" + +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Types identifiers. + * + * Variable template used to generate identifiers at compile-time for the given + * types. Use the `get` member function to know what's the identifier associated + * to the specific type. + * + * @note + * Identifiers are constant expression and can be used in any context where such + * an expression is required. As an example: + * @code{.cpp} + * using id = entt::identifier; + * + * switch(a_type_identifier) { + * case id::type: + * // ... + * break; + * case id::type: + * // ... + * break; + * default: + * // ... + * } + * @endcode + * + * @tparam Types List of types for which to generate identifiers. + */ +template +class identifier { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) ENTT_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 identifier_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr identifier_type type = 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 ENTT_NOEXCEPT { + value = val; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const ENTT_NOEXCEPT { + return value; + } + +private: + template + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template +inline monostate monostate_v = {}; + +} // namespace entt + +#endif + +// #include "core/tuple.hpp" +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @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) ENTT_NOEXCEPT { + if constexpr(std::tuple_size_v> == 1u) { + return std::get<0>(std::forward(value)); + } else { + return 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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + +// #include "core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant) ? 0u : 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 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 = (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 "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + +// #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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t: type_list {}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +struct get_t: type_list {}; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template +struct owned_t: type_list {}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; + +} // namespace entt + +#endif + + +namespace entt { + +template> +class basic_sparse_set; + +template, typename = void> +class basic_storage; + +template +class basic_registry; + +template +class basic_view; + +template +struct basic_runtime_view; + +template +class basic_group; + +template +class basic_observer; + +template +class basic_organizer; + +template +struct basic_handle; + +template +class basic_snapshot; + +template +class basic_snapshot_loader; + +template +class basic_continuous_loader; + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Alias declaration for the most common use case. */ +using sparse_set = basic_sparse_set; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using storage = basic_storage; + +/*! @brief Alias declaration for the most common use case. */ +using registry = basic_registry; + +/*! @brief Alias declaration for the most common use case. */ +using observer = basic_observer; + +/*! @brief Alias declaration for the most common use case. */ +using organizer = basic_organizer; + +/*! @brief Alias declaration for the most common use case. */ +using handle = basic_handle; + +/*! @brief Alias declaration for the most common use case. */ +using const_handle = basic_handle; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using handle_view = basic_handle; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using const_handle_view = basic_handle; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot = basic_snapshot; + +/*! @brief Alias declaration for the most common use case. */ +using snapshot_loader = basic_snapshot_loader; + +/*! @brief Alias declaration for the most common use case. */ +using continuous_loader = basic_continuous_loader; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template> +using view = basic_view; + +/*! @brief Alias declaration for the most common use case. */ +using runtime_view = basic_runtime_view; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Args Other template parameters. + */ +template +using group = basic_group; + +} // 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) ENTT_NOEXCEPT { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { + return (to_integral(value) & 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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a null object and an 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 ENTT_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 ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a tombstone object and an 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 ENTT_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 ENTT_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) ENTT_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) ENTT_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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant) ? 0u : 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 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 = (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) ENTT_NOEXCEPT { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) ENTT_NOEXCEPT { + return (to_integral(value) & 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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a null object and an 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 ENTT_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 ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const ENTT_NOEXCEPT { + return false; + } + + /** + * @brief Compares a tombstone object and an 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 ENTT_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 ENTT_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) ENTT_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) ENTT_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 "../config/config.h" + + +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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + enum class operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get + }; + + enum class policy : std::uint8_t { + owner, + ref, + cref + }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] 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 *static_cast(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) { + if constexpr(!std::is_void_v) { + info = &type_id>>(); + 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) Type{std::forward(args)...}; + } else { + new(&storage) Type(std::forward(args)...); + } + } else { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + : instance{other.data()}, + 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() ENTT_NOEXCEPT + : instance{}, + info{&type_id()}, + vtable{}, + mode{policy::owner} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : basic_any{} { + 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{} { + initialize>(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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + } + + /** + * @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) ENTT_NOEXCEPT { + return *info == req ? data() : nullptr; + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + 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 any &other) { + if(vtable && mode != policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(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); + } + + 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 ENTT_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. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + return basic_any{*this, policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return (mode == policy::owner); + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + 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) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +Type *any_cast(basic_any *data) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + // last attempt to make wrappers for const references return their values + return static_cast(static_cast, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +} // 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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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; + + sparse_set_iterator() ENTT_NOEXCEPT + : packed{}, + offset{} {} + + sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT + : packed{std::addressof(ref)}, + offset{idx} {} + + sparse_set_iterator &operator++() ENTT_NOEXCEPT { + return --offset, *this; + } + + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + sparse_set_iterator &operator--() ENTT_NOEXCEPT { + return ++offset, *this; + } + + sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + offset -= value; + return *this; + } + + sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + sparse_set_iterator copy = *this; + return (copy += value); + } + + sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return packed->data()[index() - value]; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return packed->data() + index(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() < rhs.index(); +} + +template +[[nodiscard]] bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_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(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "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 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 swap_and_pop(basic_iterator first, basic_iterator last) { + for(; first != last; ++first) { + sparse_ref(packed.back()) = entity_traits::combine(static_cast(first.index()), entity_traits::to_integral(packed.back())); + const auto entt = std::exchange(packed[first.index()], packed.back()); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = tombstone, true), ""); + // lazy self-assignment guard + sparse_ref(entt) = null; + packed.pop_back(); + } + } + + /** + * @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 in_place_pop(basic_iterator first, basic_iterator last) { + for(; first != last; ++first) { + sparse_ref(*first) = null; + packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast(first.index()), entity_traits::reserved)); + } + } + + /** + * @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 = Entity; + /*! @brief Underlying version type. */ + using version_type = typename entity_traits::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename packed_container_type::size_type; + /*! @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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.empty(); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const ENTT_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 ENTT_NOEXCEPT { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const ENTT_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 ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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. + */ + const void *get(const entity_type entt) const ENTT_NOEXCEPT { + return get_at(index(entt)); + } + + /*! @copydoc get */ + void *get(const entity_type entt) 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); + 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)); + (mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_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) { + (mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_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) { + in_place_pop(begin(), last); + } else { + for(auto &&entity: *this) { + // tombstone filter on itself + if(const auto it = find(entity); it != last) { + in_place_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 ENTT_NOEXCEPT { + return *info; + } + + /*! @brief Forwards variables to mixins, if any. */ + virtual void bind(any) ENTT_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 "../config/config.h" + +// #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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "sigh_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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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" +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + 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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_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 ENTT_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 ENTT_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 ENTT_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) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // 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; + static_assert(std::is_same_v, "Invalid value type"); + 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() + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : 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) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) ENTT_NOEXCEPT + : 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) ENTT_NOEXCEPT + : 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) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + 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) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + return calls.get_allocator(); + } + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + 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() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) ENTT_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) ENTT_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 ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @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(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {std::move(conn), signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + 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_iterator = typename Type::basic_iterator; + + template + void notify_destruction(basic_iterator first, basic_iterator last, Func func) { + 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); + func(it, it + 1u); + } + } + + void swap_and_pop(basic_iterator first, basic_iterator last) final { + notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); + } + + void in_place_pop(basic_iterator first, basic_iterator last) final { + notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); + } + + basic_iterator try_emplace(const typename 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 Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + + /*! @brief Inherited constructors. */ + using Type::Type; + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param 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 mixins, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) ENTT_NOEXCEPT final { + auto *reg = any_cast>(&value); + owner = reg ? reg : owner; + Type::bind(std::move(value)); + } + +private: + sigh &, const entity_type)> construction{}; + sigh &, const entity_type)> destruction{}; + sigh &, const entity_type)> update{}; + basic_registry *owner{}; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.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; + + storage_iterator() ENTT_NOEXCEPT = default; + + storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT + : packed{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + storage_iterator(const storage_iterator> &other) ENTT_NOEXCEPT + : packed{other.packed}, + offset{other.offset} {} + + storage_iterator &operator++() ENTT_NOEXCEPT { + return --offset, *this; + } + + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return ++(*this), orig; + } + + storage_iterator &operator--() ENTT_NOEXCEPT { + return ++offset, *this; + } + + storage_iterator operator--(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator--(), orig; + } + + storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + offset -= value; + return *this; + } + + storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator copy = *this; + return (copy += value); + } + + storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = index() - value; + return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + const auto pos = index(); + return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + return offset - 1; + } + +private: + Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() < rhs.index(); +} + +template +[[nodiscard]] bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_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; + + extended_storage_iterator() = default; + + extended_storage_iterator(It base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + extended_storage_iterator &operator++() ENTT_NOEXCEPT { + return ++std::get(it), (++std::get(it), ...), *this; + } + + extended_storage_iterator operator++(int) ENTT_NOEXCEPT { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {*std::get(it), *std::get(it)...}; + } + + template + friend bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) ENTT_NOEXCEPT; + +private: + std::tuple it; +}; + +template +[[nodiscard]] bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_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 Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The type must be at least move constructible/assignable"); + + 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; + + [[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 { + if constexpr(comp_traits::in_place_delete) { + base_type::in_place_pop(it, it + 1u); + } else { + base_type::swap_and_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(const std::size_t lhs, const std::size_t rhs) final { + using std::swap; + swap(element_at(lhs), element_at(rhs)); + } + + void move_element(const std::size_t from, const std::size_t to) final { + 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 Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ + void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + for(; first != last; ++first) { + auto &elem = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(element_at(static_cast(first.index())), std::move(elem)); + std::destroy_at(std::addressof(elem)); + base_type::swap_and_pop(first, first + 1u); + } + } + + /** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ + void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + for(; first != last; ++first) { + base_type::in_place_pop(first, first + 1u); + std::destroy_at(std::addressof(element_at(static_cast(first.index())))); + } + } + + /** + * @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. + */ + typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, 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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() ENTT_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 ENTT_NOEXCEPT { + const auto pos = static_cast(base_type::size()); + return const_iterator{&packed.first(), pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return const_iterator{&packed.first(), {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) 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 ENTT_NOEXCEPT { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) 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() ENTT_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 ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } +}; + +/** + * @brief Provides a common way to access certain properties of storage types. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects managed by the storage class. + */ +template +struct storage_traits { + /*! @brief Resulting type after component-to-storage conversion. */ + using storage_type = sigh_storage_mixin>; +}; + +} // namespace entt + +#endif + +// #include "utility.hpp" + + +namespace entt { + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.
+ * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Get Type of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + using basic_common_type = std::common_type_t::base_type...>; + + struct extended_group_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + extended_group_iterator() = default; + + extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *...> &args) + : it{from}, + pools{args} {} + + extended_group_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_group_iterator operator++(int) ENTT_NOEXCEPT { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + const auto entt = *it; + return std::tuple_cat(std::make_tuple(entt), std::get *>(pools)->get_as_tuple(entt)...); + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + typename basic_common_type::iterator it; + std::tuple *...> pools; + }; + + basic_group(basic_common_type &ref, storage_type &...gpool) ENTT_NOEXCEPT + : handler{&ref}, + pools{&gpool...} {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief 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; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : handler{} {} + + /** + * @brief Returns a const reference to the underlying handler. + * @return A const reference to the underlying handler. + */ + const base_type &handle() const ENTT_NOEXCEPT { + return *handler; + } + + /** + * @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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { + return *std::get(pools); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? handler->size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + return *this ? handler->capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + handler->shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || handler->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? handler->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return *this ? handler->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return *this ? handler->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return *this ? handler->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "Group does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)...); + } else if constexpr(sizeof...(Comp) == 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 ENTT_NOEXCEPT { + return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} + : iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function 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 Comp 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...(Comp) == 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...(Comp) == 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 Comp 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 return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + using basic_common_type = std::common_type_t::base_type..., typename storage_type::base_type...>; + + class extended_group_iterator final { + template + auto index_to_element(storage_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::tuple{}, std::declval().get({}))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + extended_group_iterator() = default; + + template + extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *..., storage_type *...> &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_group_iterator operator++(int) ENTT_NOEXCEPT { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + typename basic_common_type::iterator it; + std::tuple *..., storage_type *...> pools; + }; + + basic_group(const std::size_t &extent, storage_type &...opool, storage_type &...gpool) ENTT_NOEXCEPT + : pools{&opool..., &gpool...}, + length{&extent} {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief 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; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : length{} {} + + /** + * @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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { + return *std::get(pools); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? *length : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || !*length; + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? (std::get<0>(pools)->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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "Group does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)..., std::get *>(pools)->get_as_tuple(entt)...); + } else if constexpr(sizeof...(Comp) == 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 ENTT_NOEXCEPT { + iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; + return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are 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 Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + auto *cpool = std::get<0>(pools); + + if constexpr(sizeof...(Comp) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + cpool->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...(Comp) == 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)...)); + } + }; + + cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); + } + + [this](auto *head, auto *...other) { + for(auto next = *length; next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }(std::get *>(pools)...); + } + +private: + const std::tuple *..., storage_type *...> 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 "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../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 +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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; + + dense_map_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_map_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_map_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_map_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + dense_map_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_map_iterator operator--(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_map_iterator copy = *this; + return (copy += value); + } + + dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->element.first, it->element.second}; + } + + template + friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_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; + + dense_map_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].next, *this; + } + + dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const allocator_type &allocator) + : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_map{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 + * 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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #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 "../config/config.h" + + +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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that have at + * least the given components. Moreover, it's guaranteed that the entity list + * is tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups.
+ * Moreover, sorting a non-owning group affects all the instances of the same + * group (it means that users don't have to call `sort` on each instance to sort + * all of them because they _share_ entities and components). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Get Type of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + using basic_common_type = std::common_type_t::base_type...>; + + struct extended_group_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::tuple{}, std::declval().get({}))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + extended_group_iterator() = default; + + extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *...> &args) + : it{from}, + pools{args} {} + + extended_group_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_group_iterator operator++(int) ENTT_NOEXCEPT { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + const auto entt = *it; + return std::tuple_cat(std::make_tuple(entt), std::get *>(pools)->get_as_tuple(entt)...); + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + typename basic_common_type::iterator it; + std::tuple *...> pools; + }; + + basic_group(basic_common_type &ref, storage_type &...gpool) ENTT_NOEXCEPT + : handler{&ref}, + pools{&gpool...} {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief 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; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : handler{} {} + + /** + * @brief Returns a const reference to the underlying handler. + * @return A const reference to the underlying handler. + */ + const base_type &handle() const ENTT_NOEXCEPT { + return *handler; + } + + /** + * @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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { + return *std::get(pools); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? handler->size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const ENTT_NOEXCEPT { + return *this ? handler->capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + handler->shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || handler->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? handler->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * + * The returned iterator points to the entity following the last entity of + * the group. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return *this ? handler->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * The returned iterator points to the first entity of the reversed group. + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return *this ? handler->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * + * The returned iterator points to the entity following the last entity of + * the reversed group. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return *this ? handler->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "Group does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)...); + } else if constexpr(sizeof...(Comp) == 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 ENTT_NOEXCEPT { + return handler ? iterable{extended_group_iterator{handler->begin(), pools}, extended_group_iterator{handler->end(), pools}} + : iterable{extended_group_iterator{{}, pools}, extended_group_iterator{{}, pools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &..., const Component &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function 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 Comp 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...(Comp) == 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...(Comp) == 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 Comp 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 return all entities and only the entities that have at least + * the given components. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that the lists of owned components are tightly packed in + * memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned components and all instances have + * the same order in memory. + * + * The more types of components are owned by a group, the faster it is to + * iterate them. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @note + * Groups share references to the underlying data structures of the registry + * that generated them. Therefore any change to the entities and to the + * components made by means of the registry are immediately reflected by all the + * groups. + * Moreover, sorting an owning group affects all the instance of the same group + * (it means that users don't have to call `sort` on each instance to sort all + * of them because they share the underlying data structure). + * + * @warning + * Lifetime of a group must not overcome that of the registry that generated it. + * In any other case, attempting to use a group results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Owned Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + /*! @brief A registry is allowed to create groups. */ + friend class basic_registry; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + using basic_common_type = std::common_type_t::base_type..., typename storage_type::base_type...>; + + class extended_group_iterator final { + template + auto index_to_element(storage_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::tuple{}, std::declval().get({}))); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + + extended_group_iterator() = default; + + template + extended_group_iterator(typename basic_common_type::iterator from, const std::tuple *..., storage_type *...> &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_group_iterator operator++(int) ENTT_NOEXCEPT { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return other.it == it; + } + + [[nodiscard]] bool operator!=(const extended_group_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + + private: + typename basic_common_type::iterator it; + std::tuple *..., storage_type *...> pools; + }; + + basic_group(const std::size_t &extent, storage_type &...opool, storage_type &...gpool) ENTT_NOEXCEPT + : pools{&opool..., &gpool...}, + length{&extent} {} + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief 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; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() ENTT_NOEXCEPT + : length{} {} + + /** + * @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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_NOEXCEPT { + return *std::get(pools); + } + + /** + * @brief Returns the number of entities that have the given components. + * @return Number of entities that have the given components. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return *this ? *length : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return !*this || !*length; + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * The returned iterator points to the first entity of the group. If the + * group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return *this ? (std::get<0>(pools)->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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "Group does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::tuple_cat(std::get *>(pools)->get_as_tuple(entt)..., std::get *>(pools)->get_as_tuple(entt)...); + } else if constexpr(sizeof...(Comp) == 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 ENTT_NOEXCEPT { + iterator last = length ? std::get<0>(pools)->basic_common_type::end() : iterator{}; + return {extended_group_iterator{last - *length, pools}, extended_group_iterator{last, pools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * Sort the group so that iterating it with a couple of iterators returns + * entities and components in the expected order. See `begin` and `end` for + * more details. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Component &, const Component &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Component` are 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 Comp Optional types of components to compare. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) const { + auto *cpool = std::get<0>(pools); + + if constexpr(sizeof...(Comp) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + cpool->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...(Comp) == 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)...)); + } + }; + + cpool->sort_n(*length, std::move(comp), std::move(algo), std::forward(args)...); + } + + [this](auto *head, auto *...other) { + for(auto next = *length; next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }(std::get *>(pools)...); + } + +private: + const std::tuple *..., storage_type *...> pools; + const size_type *const length; +}; + +} // namespace entt + +#endif + +// #include "runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.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; + + runtime_view_iterator() ENTT_NOEXCEPT + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) ENTT_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 ENTT_NOEXCEPT { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { + return it == other.it; + } + + [[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_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 Runtime view implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_runtime_view; + +/** + * @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 Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct basic_runtime_view> { + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = basic_sparse_set; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() ENTT_NOEXCEPT + : pools{}, + filter{} {} + + /** + * @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(const 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(const 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: + std::vector pools; + std::vector filter; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid() const ENTT_NOEXCEPT { + return ((Component != 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; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) ENTT_NOEXCEPT + : it{curr}, + last{to}, + pools{all_of}, + filter{none_of} { + if(it != last && !valid()) { + ++(*this); + } + } + + view_iterator &operator++() ENTT_NOEXCEPT { + while(++it != last && !valid()) {} + return *this; + } + + view_iterator operator++(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return &*it; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + template + friend bool operator==(const view_iterator &, const view_iterator &) ENTT_NOEXCEPT; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; +}; + +template +[[nodiscard]] bool operator==(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const view_iterator &lhs, const view_iterator &rhs) ENTT_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; + + extended_view_iterator() = default; + + extended_view_iterator(It from, std::tuple storage) + : it{from}, + pools{storage} {} + + extended_view_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_view_iterator operator++(int) ENTT_NOEXCEPT { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { + return operator*(); + } + + template + friend bool operator==(const extended_view_iterator &, const extended_view_iterator &) ENTT_NOEXCEPT; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_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 have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +class basic_view, exclude_t> { + template + friend class basic_view; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + template + [[nodiscard]] auto pools_to_array(std::index_sequence) const ENTT_NOEXCEPT { + std::size_t pos{}; + std::array other{}; + (static_cast(std::get(pools) == view ? void() : void(other[pos++] = std::get(pools))), ...); + return other; + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Comp == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return std::get(pools)->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func func, std::index_sequence) const { + for(const auto curr: std::get(pools)->each()) { + const auto entt = std::get<0>(curr); + + if(((sizeof...(Component) != 1u) || (entt != tombstone)) + && ((Comp == Index || std::get(pools)->contains(entt)) && ...) + && std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + 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 { + ((std::get(pools) == view ? each(std::move(func), seq) : void()), ...); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = std::common_type_t::base_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() ENTT_NOEXCEPT + : pools{}, + filter{}, + view{} {} + + /** + * @brief Constructs a multi-type view from a set of storage classes. + * @param component The storage for the types to iterate. + * @param epool The storage for the types used to filter the view. + */ + basic_view(storage_type &...component, const storage_type &...epool) ENTT_NOEXCEPT + : pools{&component...}, + filter{&epool...}, + view{(std::min)({&static_cast(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} + + /** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp 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 ENTT_NOEXCEPT { + basic_view other{*this}; + other.view = std::get *>(pools); + return other; + } + + /** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp 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 ENTT_NOEXCEPT { + basic_view other{*this}; + other.view = std::get(pools); + return other; + } + + /** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ + const base_type &handle() const ENTT_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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_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 ENTT_NOEXCEPT { + return view->size(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * The returned iterator points to the first entity of the view. If the view + * is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + } + + /** + * @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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for{}), filter} : 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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); + } else if constexpr(sizeof...(Comp) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple_cat(std::get *>(pools)->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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Other) == 0) { + return std::get(pools)->get(entt); + } else { + return std::tuple_cat(std::get(pools)->get_as_tuple(entt), 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 + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + pick_and_each(std::move(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 ENTT_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 Get Component list of the view to combine with. + * @tparam Excl 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 ENTT_NOEXCEPT { + using view_type = basic_view, exclude_t>; + return std::make_from_tuple(std::tuple_cat( + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, filter), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + } + +private: + std::tuple *...> pools; + std::array 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 instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pool iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>::in_place_delete>>> { + template + friend class basic_view; + + using storage_type = constness_as_t>::storage_type, Component>; + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = typename storage_type::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() ENTT_NOEXCEPT + : pools{}, + filter{}, + view{} {} + + /** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ + basic_view(storage_type &ref) ENTT_NOEXCEPT + : pools{&ref}, + filter{}, + view{&ref} {} + + /** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ + const base_type &handle() const ENTT_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 ENTT_NOEXCEPT { + static_assert(std::is_same_v, "Invalid component type"); + return *std::get<0>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_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 ENTT_NOEXCEPT { + return view->size(); + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return view->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 ENTT_NOEXCEPT { + return view->begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return view->end(); + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * The returned iterator points to the first entity of the reversed view. If + * the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return view->rbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * + * The returned iterator points to the entity following the last entity of + * the reversed view. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return view->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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return contains(entt) ? view->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 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 ENTT_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 ENTT_NOEXCEPT { + return view->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 Comp 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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::get<0>(pools)->get_as_tuple(entt); + } else { + static_assert(std::is_same_v, "Invalid component type"); + return std::get<0>(pools)->get(entt); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + return std::get<0>(pools)->get(entt); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @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(std::is_invocable_v) { + for(auto &&component: *std::get<0>(pools)) { + func(component); + } + } else if constexpr(std::is_invocable_v) { + for(auto entity: *view) { + func(entity); + } + } else { + for(size_type pos{}, last = size(); pos < last; ++pos) { + func(); + } + } + } + + /** + * @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 ENTT_NOEXCEPT { + return std::get<0>(pools)->each(); + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl 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 ENTT_NOEXCEPT { + using view_type = basic_view, exclude_t>; + return std::make_from_tuple(std::tuple_cat( + std::forward_as_tuple(*std::get<0>(pools)), + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + } + +private: + std::tuple pools; + std::array filter; + const base_type *view; +}; + +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Storage &...storage) -> basic_view, get_t...>, exclude_t<>>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class storage_proxy_iterator final { + template + friend class storage_proxy_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; + + storage_proxy_iterator() ENTT_NOEXCEPT + : it{} {} + + storage_proxy_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + storage_proxy_iterator(const storage_proxy_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + storage_proxy_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { + storage_proxy_iterator orig = *this; + return ++(*this), orig; + } + + storage_proxy_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { + storage_proxy_iterator orig = *this; + return operator--(), orig; + } + + storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_proxy_iterator copy = *this; + return (copy += value); + } + + storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->first, *it->second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + template + friend std::ptrdiff_t operator-(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +struct registry_context { + template + Type &emplace_hint(const id_type id, Args &&...args) { + return any_cast(data.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_hint(type_id().hash(), std::forward(args)...); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = data.find(id); + return it != data.end() && it->second.type() == type_id() ? (data.erase(it), true) : false; + } + + template + [[nodiscard]] std::add_const_t &at(const id_type id = type_id().hash()) const { + return any_cast &>(data.at(id)); + } + + template + [[nodiscard]] Type &at(const id_type id = type_id().hash()) { + return any_cast(data.at(id)); + } + + template + [[nodiscard]] std::add_const_t *find(const id_type id = type_id().hash()) const { + const auto it = data.find(id); + return it != data.cend() ? any_cast>(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = data.find(id); + return it != data.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = data.find(id); + return it != data.end() && it->second.type() == type_id(); + } + +private: + dense_map, identity> data; +}; + +} // 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). + */ +template +class basic_registry { + using entity_traits = entt_traits; + using basic_common_type = basic_sparse_set; + + template + using storage_type = typename storage_traits::storage_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"); + std::conditional_t current{}; + + 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::unique_ptr group; + bool (*owned)(const id_type) ENTT_NOEXCEPT; + bool (*get)(const id_type) ENTT_NOEXCEPT; + bool (*exclude)(const id_type) ENTT_NOEXCEPT; + }; + + template + [[nodiscard]] 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.reset(new storage_type{}); + 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_type placeholder{}; + return placeholder; + } + + auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { + ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); + return entity_traits::combine(static_cast(pos), {}); + } + + auto recycle_identifier() ENTT_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(entities[curr]), tombstone); + return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); + } + + auto release_entity(const Entity entity, const typename entity_traits::version_type version) { + const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); + entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); + free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone); + return vers; + } + +public: + /*! @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() + : pools{}, + groups{}, + entities{}, + free_list{tombstone}, + vars{} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + */ + basic_registry(const size_type count) + : pools{}, + groups{}, + entities{}, + free_list{tombstone}, + vars{} { + pools.reserve(count); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) + : pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)}, + free_list{other.free_list}, + vars{std::move(other.vars)} { + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) { + pools = std::move(other.pools); + groups = std::move(other.groups); + entities = std::move(other.entities); + free_list = other.free_list; + vars = std::move(other.vars); + + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + + return *this; + } + + /** + * @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() ENTT_NOEXCEPT { + return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; + } + + /*! @copydoc storage */ + [[nodiscard]] auto storage() const ENTT_NOEXCEPT { + return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_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 An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] auto storage(const id_type id) { + return internal::storage_proxy_iterator{pools.find(id)}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] auto storage(const id_type id) const { + return internal::storage_proxy_iterator{pools.find(id)}; + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Component 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()) { + if constexpr(std::is_const_v) { + return std::as_const(*this).template storage>(id); + } else { + 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 Component 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 ENTT_NOEXCEPT { + return entities.size(); + } + + /** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ + [[nodiscard]] size_type alive() const { + auto sz = entities.size(); + + for(auto curr = free_list; curr != null; --sz) { + curr = entities[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) { + entities.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 ENTT_NOEXCEPT { + return entities.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 ENTT_NOEXCEPT { + return entities.data(); + } + + /** + * @brief Returns the head of the list of released entities. + * + * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of released entities. + */ + [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { + return free_list; + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entity An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entity) const { + const auto pos = size_type(entity_traits::to_entity(entity)); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entity A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entity) const { + const auto pos = size_type(entity_traits::to_entity(entity)); + return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return (free_list == null) ? entities.emplace_back(generate_identifier(entities.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 = entities.size(); + + if(hint == null || hint == tombstone) { + return create(); + } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { + entities.resize(size_type(req) + 1u, null); + + for(auto pos = length; pos < req; ++pos) { + release_entity(generate_identifier(pos), {}); + } + + return (entities[req] = hint); + } else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { + return create(); + } else { + auto *it = &free_list; + for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {} + *it = entity_traits::combine(curr, entity_traits::to_integral(*it)); + return (entities[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 = entities.size(); + entities.resize(length + std::distance(first, last), null); + + for(auto pos = length; first != last; ++first, ++pos) { + *first = entities[pos] = generate_identifier(pos); + } + } + + /** + * @brief Assigns identifiers to an empty registry. + * + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.
+ * Don't try to inject ranges of randomly generated entities nor the _wrong_ + * head for the list of destroyed entities. There is no guarantee that a + * registry will continue to work properly in this case. + * + * @warning + * There must be no entities still alive for this to work properly. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param destroyed The head of the list of destroyed entities. + */ + template + void assign(It first, It last, const entity_type destroyed) { + ENTT_ASSERT(!alive(), "Entities still alive"); + entities.assign(first, last); + free_list = destroyed; + } + + /** + * @brief Releases an identifier. + * + * The version is updated and the identifier can be recycled at any time. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ + version_type release(const entity_type entity) { + return release(entity, static_cast(entity_traits::to_version(entity) + 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 entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type release(const entity_type entity, const version_type version) { + ENTT_ASSERT(orphan(entity), "Non-orphan entity"); + return release_entity(entity, version); + } + + /** + * @brief Releases all 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 entity A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entity) { + return destroy(entity, static_cast(entity_traits::to_version(entity) + 1u)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entity, const version_type version) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entity); + } + + return release_entity(entity, version); + } + + /** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void destroy(It first, It last) { + 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 use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().emplace(entity, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ + template + void insert(It first, It last, const Component &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().insert(first, last, value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + */ + template::value_type, Component>>> + void insert(EIt first, EIt last, CIt from) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + auto &cpool = assure(); + + return cpool.contains(entity) + ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool.emplace(entity, 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(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch a component of an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entity, Func &&...func) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().patch(entity, 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 use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entity A valid identifier. + * @return The number of components actually removed. + */ + template + size_type remove(const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure().remove(entity) + ... + assure().remove(entity)); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Component 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) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + 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) { + ENTT_ASSERT(valid(*first), "Invalid entity"); + 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 use an invalid entity or to erase a component from an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entity A valid identifier. + */ + template + void erase(const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + (assure().erase(entity), (assure().erase(entity), ...)); + } + + /** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Component 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) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().erase(std::move(first), std::move(last)); + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + ENTT_ASSERT(valid(*first), "Invalid entity"); + 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 Component Types of components for which to clear all tombstones. + */ + template + void compact() { + if constexpr(sizeof...(Component) == 0) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure().compact(), ...); + } + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + [[nodiscard]] bool all_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure>().contains(entity) && ...); + } + + /** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] bool any_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure>().contains(entity) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return view().template get(entity); + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return view().template get(entity); + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + auto &cpool = assure(); + return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + + if constexpr(sizeof...(Component) == 1) { + const auto &cpool = assure...>(); + return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { + if constexpr(sizeof...(Component) == 1) { + return (const_cast(std::as_const(*this).template try_get(entity)), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Component) == 0) { + for(auto &&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 = entities.size(); pos; --pos) { + func(entities[pos - 1]); + } + } else { + for(auto pos = entities.size(); pos; --pos) { + if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { + func(entity); + } + } + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entity A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); + } + + /** + * @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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct() { + return assure().on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * 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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update() { + return assure().on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * 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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy() { + return assure().on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * + * 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 Component 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, std::add_const_t...>, exclude_t> view(exclude_t = {}) const { + return {assure>(), assure>()..., assure()...}; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, 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 Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t, 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, + {new handler_type{}, [](void *instance) { delete static_cast(instance); }}, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, + }; + + handler = static_cast(candidate.group.get()); + + const void *maybe_valid_if = nullptr; + const void *discard_if = nullptr; + + if constexpr(sizeof...(Owned) == 0) { + groups.push_back(std::move(candidate)); + } else { + [[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>()...}; + } + } + + /*! @copydoc group */ + template + [[nodiscard]] basic_group, get_t<>, exclude_t> group(exclude_t = {}) { + return group(get_t<>{}, exclude); + } + + /*! @copydoc group */ + template + [[nodiscard]] basic_group...>, get_t<>, exclude_t> group_if_exists(exclude_t = {}) const { + return group_if_exists...>(get_t<>{}, exclude); + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are _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 Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return True if the group can be sorted, false otherwise. + */ + template + [[nodiscard]] bool sortable(const basic_group, get_t, exclude_t> &) ENTT_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 Component &, const Component &); + * @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 Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT(!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() ENTT_NOEXCEPT { + return vars; + } + + /*! @copydoc ctx */ + const context &ctx() const ENTT_NOEXCEPT { + return vars; + } + +private: + dense_map, identity> pools; + std::vector groups; + std::vector entities; + entity_type free_list; + context vars; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Types to which to restrict the scope of a handle. + */ +template +struct basic_handle { + /*! @brief Type of registry accepted by the handle. */ + using registry_type = constness_as_t>, Entity>; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename registry_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename registry_type::size_type; + + /*! @brief Constructs an invalid handle. */ + basic_handle() ENTT_NOEXCEPT + : reg{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) ENTT_NOEXCEPT + : reg{&ref}, + entt{value} {} + + /** + * @brief Constructs a const handle from a non-const one. + * @tparam Other A valid entity type (see entt_traits for more details). + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template + operator basic_handle() const ENTT_NOEXCEPT { + static_assert(std::is_same_v || std::is_same_v, Entity>, "Invalid conversion between different handles"); + static_assert((sizeof...(Type) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Type)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + + return reg ? basic_handle{*reg, entt} : basic_handle{}; + } + + /** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ + [[nodiscard]] operator entity_type() const ENTT_NOEXCEPT { + return entity(); + } + + /** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return reg && reg->valid(entt); + } + + /** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ + [[nodiscard]] bool valid() const { + return reg->valid(entt); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const ENTT_NOEXCEPT { + return reg; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const ENTT_NOEXCEPT { + return entt; + } + + /** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + */ + void destroy() { + reg->destroy(entt); + } + + /** + * @brief Destroys the entity associated with a handle. + * @sa basic_registry::destroy + * @param version A desired version upon destruction. + */ + void destroy(const version_type version) { + reg->destroy(entt, version); + } + + /** + * @brief Assigns the given component to a handle. + * @sa basic_registry::emplace + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns or replaces the given component for a handle. + * @sa basic_registry::emplace_or_replace + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given component for a handle. + * @sa basic_registry::patch + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for a handle. + * @sa basic_registry::replace + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given components from a handle. + * @sa basic_registry::remove + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ + template + size_type remove() const { + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template remove(entt); + } + + /** + * @brief Erases the given components from a handle. + * @sa basic_registry::erase + * @tparam Component Types of components to erase. + */ + template + void erase() const { + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template erase(entt); + } + + /** + * @brief Checks if a handle has all the given components. + * @sa basic_registry::all_of + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. + */ + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); + } + + /** + * @brief Checks if a handle has at least one of the given components. + * @sa basic_registry::any_of + * @tparam Component Components for which to perform the check. + * @return True if the handle has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); + } + + /** + * @brief Returns references to the given components for a handle. + * @sa basic_registry::get + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); + } + + /** + * @brief Returns a reference to the given component for a handle. + * @sa basic_registry::get_or_emplace + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Type) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template get_or_emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for a handle. + * @sa basic_registry::try_get + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. + */ + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Type) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template try_get(entt); + } + + /** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return reg->orphan(entt); + } + + /** + * @brief Visits a handle and returns the pools for its components. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(id_type, const basic_sparse_set &); + * @endcode + * + * Returned pools are those that contain the entity associated with the + * handle. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void visit(Func &&func) const { + for(auto [id, storage]: reg->storage()) { + if(storage.contains(entt)) { + func(id, storage); + } + } + } + +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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +basic_handle(basic_registry &, Entity) -> basic_handle; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +basic_handle(const basic_registry &, Entity) -> basic_handle; + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../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 +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + 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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_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 ENTT_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 ENTT_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 ENTT_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) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "registry.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_view { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, Entity>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ + template + operator basic_view, Exclude>() const { + return reg.template view(Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(basic_registry &) -> as_view; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(const basic_registry &) -> as_view; + +/** + * @brief Converts a registry to a group. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_group { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, Entity>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ + template + operator basic_group, Get, Exclude>() const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(Get{}, Exclude{}); + } else { + return reg.template group(Get{}, Exclude{}); + } + } + +private: + registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(basic_registry &) -> as_group; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(const basic_registry &) -> as_group; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template +void invoke(basic_registry ®, const Entity entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate &, const Entity)> func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default pool as it + * makes assumptions about how the components are laid out. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +Entity to_entity(const basic_registry ®, const Component &instance) { + const auto &storage = reg.template storage(); + const typename basic_registry::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { + 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 "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + +namespace entt { + +/*! @brief Grouping matcher. */ +template +struct matcher {}; + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_collector; + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.
+ * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>>{}; + } +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template +struct basic_collector, type_list, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher, type_list, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = {}) ENTT_NOEXCEPT { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() ENTT_NOEXCEPT { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = {}) ENTT_NOEXCEPT { + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; + } +}; + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.
+ * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.
+ * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators and using them results in undefined + * behavior. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_observer { + using payload_type = std::uint32_t; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { + if(reg.template all_of(entt) && !reg.template any_of(entt)) { + if(!obs.storage.contains(entt)) { + obs.storage.emplace(entt); + } + + obs.storage.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { + if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { + obs.storage.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + reg.template on_update().template connect<&maybe_valid_if>(obs); + reg.template on_destroy().template connect<&discard_if>(obs); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + reg.template on_update().disconnect(obs); + reg.template on_destroy().disconnect(obs); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(basic_observer &obs, basic_registry ®, const Entity entt) { + 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.storage.contains(entt)) { + obs.storage.emplace(entt); + } + + obs.storage.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, basic_registry &, const Entity entt) { + if(obs.storage.contains(entt) && !(obs.storage.get(entt) &= (~(1 << Index)))) { + obs.storage.erase(entt); + } + } + + template + static void connect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + } + + static void disconnect(basic_observer &obs, basic_registry ®) { + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_destroy().disconnect(obs), ...); + (reg.template on_construct().disconnect(obs), ...); + } + }; + + template + static void disconnect(basic_registry ®, basic_observer &obs) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(basic_registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(*this, reg), ...); + release.template connect<&basic_observer::disconnect>(reg); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename basic_sparse_set::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : release{}, + storage{} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + basic_observer(basic_registry ®, basic_collector) + : basic_observer{} { + connect(reg, std::index_sequence_for{}); + } + + /*! @brief Default destructor. */ + ~basic_observer() = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(const basic_observer &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(basic_observer &&) = delete; + + /** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + void connect(basic_registry ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + storage.clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this); + release.reset(); + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return storage.size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return storage.empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type *data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * The returned iterator points to the first entity of the observer. If the + * container is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return storage.basic_sparse_set::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * + * The returned iterator points to the entity following the last entity of + * the observer. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return storage.basic_sparse_set::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() ENTT_NOEXCEPT { + storage.clear(); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.
+ * The signature of the function must be equivalent to the following form: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + + /** + * @brief Iterates entities and applies the given function object to them, + * then clears the observer. + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) { + std::as_const(*this).each(std::move(func)); + clear(); + } + +private: + delegate release; + basic_storage storage; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "registry.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_view { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, Entity>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Exclude Types of components used to filter the view. + * @tparam Component Type of components used to construct the view. + * @return A newly created view. + */ + template + operator basic_view, Exclude>() const { + return reg.template view(Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(basic_registry &) -> as_view; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_view(const basic_registry &) -> as_view; + +/** + * @brief Converts a registry to a group. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +struct as_group { + /*! @brief Underlying entity identifier. */ + using entity_type = std::remove_const_t; + /*! @brief Type of registry to convert. */ + using registry_type = constness_as_t, Entity>; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) ENTT_NOEXCEPT: reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @tparam Owned Types of components owned by the group. + * @return A newly created group. + */ + template + operator basic_group, Get, Exclude>() const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(Get{}, Exclude{}); + } else { + return reg.template group(Get{}, Exclude{}); + } + } + +private: + registry_type ® +}; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(basic_registry &) -> as_group; + +/** + * @brief Deduction guide. + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +as_group(const basic_registry &) -> as_group; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template +void invoke(basic_registry ®, const Entity entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate &, const Entity)> func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default pool as it + * makes assumptions about how the components are laid out. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component. + * @param reg A registry that contains the given entity and its components. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +Entity to_entity(const basic_registry ®, const Component &instance) { + const auto &storage = reg.template storage(); + const typename basic_registry::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += ENTT_PACKED_PAGE) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < ENTT_PACKED_PAGE) { + 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, exclude_t>>: 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>::ro...>; + using rw = type_list_cat_t>::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 Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_organizer final { + using callback_type = void(const void *, basic_registry &); + using prepare_type = void(basic_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(basic_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(basic_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 = (std::min)(count, sizeof...(Type)); + std::copy_n(info, length, buffer); + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { + dependencies[type_hash>::value()].emplace_back(index, requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (dependencies[type_hash::value()].emplace_back(index, false), ...); + (dependencies[type_hash::value()].emplace_back(index, true), ...); + } + + [[nodiscard]] std::vector adjacency_matrix() { + const auto length = vertices.size(); + std::vector edges(length * length, false); + + // creates the adjacency matrix + for(const auto &deps: dependencies) { + const auto last = deps.second.cend(); + auto it = deps.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + edges[curr->first * length + it->first] = true; + } else { + if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { + for(; it != next; ++it) { + edges[curr->first * length + it->first] = true; + edges[it->first * length + next->first] = true; + } + } else { + for(; it != next; ++it) { + edges[curr->first * length + it->first] = true; + } + } + } + } + } else { + // ro item, possibly only on first iteration + if(const auto next = std::find_if(it, last, [](const auto &elem) { return elem.second; }); next != last) { + for(; it != next; ++it) { + edges[it->first * length + next->first] = true; + } + } else { + it = last; + } + } + } + } + + // computes the transitive closure + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + edges[vi * length + vj] = edges[vi * length + vj] || (edges[vi * length + vk] && edges[vk * length + vj]); + } + } + } + + // applies the transitive reduction + for(std::size_t vert{}; vert < length; ++vert) { + edges[vert * length + vert] = false; + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(edges[vi * length + vj]) { + for(std::size_t vk{}; vk < length; ++vk) { + if(edges[vj * length + vk]) { + edges[vi * length + vk] = false; + } + } + } + } + } + + return edges; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ + vertex(const bool vtype, vertex_data data, std::vector edges) + : is_top_level{vtype}, + node{std::move(data)}, + reachable{std::move(edges)} {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type ro_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type rw_dependency(const type_info **buffer, const std::size_t length) const ENTT_NOEXCEPT { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + size_type ro_count() const ENTT_NOEXCEPT { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + size_type rw_count() const ENTT_NOEXCEPT { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + bool top_level() const ENTT_NOEXCEPT { + return is_top_level; + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + const type_info &info() const ENTT_NOEXCEPT { + return *node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + const char *name() const ENTT_NOEXCEPT { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + function_type *callback() const ENTT_NOEXCEPT { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + const void *data() const ENTT_NOEXCEPT { + return node.payload; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + const std::vector &children() const ENTT_NOEXCEPT { + return reachable; + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(basic_registry ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + bool is_top_level; + vertex_data node; + std::vector reachable; + }; + + /** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ + template + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v>; + + callback_type *callback = +[](const void *, basic_registry ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + 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); }, + +[](basic_registry ®) { 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, basic_registry ®) { + Type *curr = static_cast(const_cast *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + 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); }, + +[](basic_registry ®) { 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() { + const auto edges = adjacency_matrix(); + + // creates the adjacency list + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + + for(std::size_t col{}, length = vertices.size(); col < length; ++col) { + std::vector reachable{}; + const auto row = col * length; + bool is_top_level = true; + + for(std::size_t next{}; next < length; ++next) { + if(edges[row + next]) { + reachable.push_back(next); + } + } + + for(std::size_t next{}; next < length && is_top_level; ++next) { + is_top_level = !edges[next * length + col]; + } + + adjacency_list.emplace_back(is_top_level, vertices[col], std::move(reachable)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + dependencies.clear(); + vertices.clear(); + } + +private: + dense_map>, identity> dependencies; + std::vector vertices; +}; + +} // 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 "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/algorithm.hpp" + +// #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 "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "runtime_view.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + +// #include "view.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class storage_proxy_iterator final { + template + friend class storage_proxy_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; + + storage_proxy_iterator() ENTT_NOEXCEPT + : it{} {} + + storage_proxy_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + storage_proxy_iterator(const storage_proxy_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + storage_proxy_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + storage_proxy_iterator operator++(int) ENTT_NOEXCEPT { + storage_proxy_iterator orig = *this; + return ++(*this), orig; + } + + storage_proxy_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + storage_proxy_iterator operator--(int) ENTT_NOEXCEPT { + storage_proxy_iterator orig = *this; + return operator--(), orig; + } + + storage_proxy_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + storage_proxy_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_proxy_iterator copy = *this; + return (copy += value); + } + + storage_proxy_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_proxy_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->first, *it->second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + template + friend std::ptrdiff_t operator-(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const storage_proxy_iterator &, const storage_proxy_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const storage_proxy_iterator &lhs, const storage_proxy_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +struct registry_context { + template + Type &emplace_hint(const id_type id, Args &&...args) { + return any_cast(data.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_hint(type_id().hash(), std::forward(args)...); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = data.find(id); + return it != data.end() && it->second.type() == type_id() ? (data.erase(it), true) : false; + } + + template + [[nodiscard]] std::add_const_t &at(const id_type id = type_id().hash()) const { + return any_cast &>(data.at(id)); + } + + template + [[nodiscard]] Type &at(const id_type id = type_id().hash()) { + return any_cast(data.at(id)); + } + + template + [[nodiscard]] std::add_const_t *find(const id_type id = type_id().hash()) const { + const auto it = data.find(id); + return it != data.cend() ? any_cast>(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = data.find(id); + return it != data.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = data.find(id); + return it != data.end() && it->second.type() == type_id(); + } + +private: + dense_map, identity> data; +}; + +} // 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). + */ +template +class basic_registry { + using entity_traits = entt_traits; + using basic_common_type = basic_sparse_set; + + template + using storage_type = typename storage_traits::storage_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"); + std::conditional_t current{}; + + 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::unique_ptr group; + bool (*owned)(const id_type) ENTT_NOEXCEPT; + bool (*get)(const id_type) ENTT_NOEXCEPT; + bool (*exclude)(const id_type) ENTT_NOEXCEPT; + }; + + template + [[nodiscard]] 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.reset(new storage_type{}); + 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_type placeholder{}; + return placeholder; + } + + auto generate_identifier(const std::size_t pos) ENTT_NOEXCEPT { + ENTT_ASSERT(pos < entity_traits::to_integral(null), "No entities available"); + return entity_traits::combine(static_cast(pos), {}); + } + + auto recycle_identifier() ENTT_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(entities[curr]), tombstone); + return (entities[curr] = entity_traits::combine(curr, entity_traits::to_integral(entities[curr]))); + } + + auto release_entity(const Entity entity, const typename entity_traits::version_type version) { + const typename entity_traits::version_type vers = version + (version == entity_traits::to_version(tombstone)); + entities[entity_traits::to_entity(entity)] = entity_traits::construct(entity_traits::to_integral(free_list), vers); + free_list = entity_traits::combine(entity_traits::to_integral(entity), tombstone); + return vers; + } + +public: + /*! @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() + : pools{}, + groups{}, + entities{}, + free_list{tombstone}, + vars{} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + */ + basic_registry(const size_type count) + : pools{}, + groups{}, + entities{}, + free_list{tombstone}, + vars{} { + pools.reserve(count); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) + : pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)}, + free_list{other.free_list}, + vars{std::move(other.vars)} { + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) { + pools = std::move(other.pools); + groups = std::move(other.groups); + entities = std::move(other.entities); + free_list = other.free_list; + vars = std::move(other.vars); + + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + + return *this; + } + + /** + * @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() ENTT_NOEXCEPT { + return iterable_adaptor{internal::storage_proxy_iterator{pools.begin()}, internal::storage_proxy_iterator{pools.end()}}; + } + + /*! @copydoc storage */ + [[nodiscard]] auto storage() const ENTT_NOEXCEPT { + return iterable_adaptor{internal::storage_proxy_iterator{pools.cbegin()}, internal::storage_proxy_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 An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] auto storage(const id_type id) { + return internal::storage_proxy_iterator{pools.find(id)}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return An iterator to the given storage if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] auto storage(const id_type id) const { + return internal::storage_proxy_iterator{pools.find(id)}; + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Component 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()) { + if constexpr(std::is_const_v) { + return std::as_const(*this).template storage>(id); + } else { + 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 Component 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 ENTT_NOEXCEPT { + return entities.size(); + } + + /** + * @brief Returns the number of entities still in use. + * @return Number of entities still in use. + */ + [[nodiscard]] size_type alive() const { + auto sz = entities.size(); + + for(auto curr = free_list; curr != null; --sz) { + curr = entities[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) { + entities.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 ENTT_NOEXCEPT { + return entities.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 ENTT_NOEXCEPT { + return entities.data(); + } + + /** + * @brief Returns the head of the list of released entities. + * + * This function is intended for use in conjunction with `assign`.
+ * The returned entity has an invalid identifier in all cases. + * + * @return The head of the list of released entities. + */ + [[nodiscard]] entity_type released() const ENTT_NOEXCEPT { + return free_list; + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entity An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entity) const { + const auto pos = size_type(entity_traits::to_entity(entity)); + return (pos < entities.size() && entities[pos] == entity); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entity A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entity) const { + const auto pos = size_type(entity_traits::to_entity(entity)); + return entity_traits::to_version(pos < entities.size() ? entities[pos] : tombstone); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return (free_list == null) ? entities.emplace_back(generate_identifier(entities.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 = entities.size(); + + if(hint == null || hint == tombstone) { + return create(); + } else if(const auto req = entity_traits::to_entity(hint); !(req < length)) { + entities.resize(size_type(req) + 1u, null); + + for(auto pos = length; pos < req; ++pos) { + release_entity(generate_identifier(pos), {}); + } + + return (entities[req] = hint); + } else if(const auto curr = entity_traits::to_entity(entities[req]); req == curr) { + return create(); + } else { + auto *it = &free_list; + for(; entity_traits::to_entity(*it) != req; it = &entities[entity_traits::to_entity(*it)]) {} + *it = entity_traits::combine(curr, entity_traits::to_integral(*it)); + return (entities[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 = entities.size(); + entities.resize(length + std::distance(first, last), null); + + for(auto pos = length; first != last; ++first, ++pos) { + *first = entities[pos] = generate_identifier(pos); + } + } + + /** + * @brief Assigns identifiers to an empty registry. + * + * This function is intended for use in conjunction with `data`, `size` and + * `destroyed`.
+ * Don't try to inject ranges of randomly generated entities nor the _wrong_ + * head for the list of destroyed entities. There is no guarantee that a + * registry will continue to work properly in this case. + * + * @warning + * There must be no entities still alive for this to work properly. + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param destroyed The head of the list of destroyed entities. + */ + template + void assign(It first, It last, const entity_type destroyed) { + ENTT_ASSERT(!alive(), "Entities still alive"); + entities.assign(first, last); + free_list = destroyed; + } + + /** + * @brief Releases an identifier. + * + * The version is updated and the identifier can be recycled at any time. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @param entity A valid identifier. + * @return The version of the recycled entity. + */ + version_type release(const entity_type entity) { + return release(entity, static_cast(entity_traits::to_version(entity) + 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 entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type release(const entity_type entity, const version_type version) { + ENTT_ASSERT(orphan(entity), "Non-orphan entity"); + return release_entity(entity, version); + } + + /** + * @brief Releases all 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 entity A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entity) { + return destroy(entity, static_cast(entity_traits::to_version(entity) + 1u)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entity A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entity, const version_type version) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entity); + } + + return release_entity(entity, version); + } + + /** + * @brief Destroys all entities in a range and releases their identifiers. + * + * @sa destroy + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void destroy(It first, It last) { + 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 use an invalid entity or to assign a component to an entity + * that already owns it results in undefined behavior. + * + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().emplace(entity, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ + template + void insert(It first, It last, const Component &value = {}) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().insert(first, last, value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Component Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + */ + template::value_type, Component>>> + void insert(EIt first, EIt last, CIt from) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + auto &cpool = assure(); + + return cpool.contains(entity) + ? cpool.patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }) + : cpool.emplace(entity, 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(Component &); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned. However, this function can be used to trigger an update signal + * for them. + * + * @warning + * Attempting to use an invalid entity or to patch a component of an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entity A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entity, Func &&...func) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().patch(entity, 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 use an invalid entity or to replace a component of an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return assure().patch(entity, [&args...](auto &...curr) { ((curr = Component{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given components from an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entity A valid identifier. + * @return The number of components actually removed. + */ + template + size_type remove(const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure().remove(entity) + ... + assure().remove(entity)); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Component 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) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + 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) { + ENTT_ASSERT(valid(*first), "Invalid entity"); + 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 use an invalid entity or to erase a component from an + * entity that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entity A valid identifier. + */ + template + void erase(const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + (assure().erase(entity), (assure().erase(entity), ...)); + } + + /** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Component 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) { + ENTT_ASSERT(std::all_of(first, last, [this](const auto entity) { return valid(entity); }), "Invalid entity"); + assure().erase(std::move(first), std::move(last)); + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + ENTT_ASSERT(valid(*first), "Invalid entity"); + 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 Component Types of components for which to clear all tombstones. + */ + template + void compact() { + if constexpr(sizeof...(Component) == 0) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure().compact(), ...); + } + } + + /** + * @brief Checks if an entity has all the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has all the components, false otherwise. + */ + template + [[nodiscard]] bool all_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure>().contains(entity) && ...); + } + + /** + * @brief Checks if an entity has at least one of the given components. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Components for which to perform the check. + * @param entity A valid identifier. + * @return True if the entity has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] bool any_of(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return (assure>().contains(entity) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity or to get a component from an entity + * that doesn't own it results in undefined behavior. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return view().template get(entity); + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entity) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return view().template get(entity); + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entity A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entity, Args &&...args) { + ENTT_ASSERT(valid(entity), "Invalid entity"); + auto &cpool = assure(); + return cpool.contains(entity) ? cpool.get(entity) : cpool.emplace(entity, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @warning + * Attempting to use an invalid entity results in undefined behavior. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Component Types of components to get. + * @param entity A valid identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + + if constexpr(sizeof...(Component) == 1) { + const auto &cpool = assure...>(); + return cpool.contains(entity) ? std::addressof(cpool.get(entity)) : nullptr; + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entity) { + if constexpr(sizeof...(Component) == 1) { + return (const_cast(std::as_const(*this).template try_get(entity)), ...); + } else { + return std::make_tuple(try_get(entity)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Component Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Component) == 0) { + for(auto &&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 = entities.size(); pos; --pos) { + func(entities[pos - 1]); + } + } else { + for(auto pos = entities.size(); pos; --pos) { + if(const auto entity = entities[pos - 1]; entity_traits::to_entity(entity) == (pos - 1)) { + func(entity); + } + } + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entity A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entity) const { + ENTT_ASSERT(valid(entity), "Invalid entity"); + return std::none_of(pools.cbegin(), pools.cend(), [entity](auto &&curr) { return curr.second->contains(entity); }); + } + + /** + * @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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct() { + return assure().on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * 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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update() { + return assure().on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * 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 Component Type of component of which to get the sink. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy() { + return assure().on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * + * 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 Component 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, std::add_const_t...>, exclude_t> view(exclude_t = {}) const { + return {assure>(), assure>()..., assure()...}; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, 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 Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return A newly created group. + */ + template + [[nodiscard]] basic_group, get_t, 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, + {new handler_type{}, [](void *instance) { delete static_cast(instance); }}, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash>::value()) || ...); }, + []([[maybe_unused]] const id_type ctype) ENTT_NOEXCEPT { return ((ctype == type_hash::value()) || ...); }, + }; + + handler = static_cast(candidate.group.get()); + + const void *maybe_valid_if = nullptr; + const void *discard_if = nullptr; + + if constexpr(sizeof...(Owned) == 0) { + groups.push_back(std::move(candidate)); + } else { + [[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>()...}; + } + } + + /*! @copydoc group */ + template + [[nodiscard]] basic_group, get_t<>, exclude_t> group(exclude_t = {}) { + return group(get_t<>{}, exclude); + } + + /*! @copydoc group */ + template + [[nodiscard]] basic_group...>, get_t<>, exclude_t> group_if_exists(exclude_t = {}) const { + return group_if_exists...>(get_t<>{}, exclude); + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Component Types of components in which one is interested. + * @return True if the pools of the given components are _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 Types of components owned by the group. + * @tparam Get Types of components observed by the group. + * @tparam Exclude Types of components used to filter the group. + * @return True if the group can be sorted, false otherwise. + */ + template + [[nodiscard]] bool sortable(const basic_group, get_t, exclude_t> &) ENTT_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 Component &, const Component &); + * @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 Component Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT(!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() ENTT_NOEXCEPT { + return vars; + } + + /*! @copydoc ctx */ + const context &ctx() const ENTT_NOEXCEPT { + return vars; + } + +private: + dense_map, identity> pools; + std::vector groups; + std::vector entities; + entity_type free_list; + context vars; +}; + +} // 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 "../config/config.h" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.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; + + runtime_view_iterator() ENTT_NOEXCEPT + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) ENTT_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 ENTT_NOEXCEPT { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] bool operator==(const runtime_view_iterator &other) const ENTT_NOEXCEPT { + return it == other.it; + } + + [[nodiscard]] bool operator!=(const runtime_view_iterator &other) const ENTT_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 Runtime view implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_runtime_view; + +/** + * @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 Entity A valid entity type (see entt_traits for more details). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct basic_runtime_view> { + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = basic_sparse_set; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() ENTT_NOEXCEPT + : pools{}, + filter{} {} + + /** + * @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(const 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(const 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: + std::vector pools; + std::vector filter; +}; + +} // namespace entt + +#endif + +// #include "entity/sigh_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_iterator = typename Type::basic_iterator; + + template + void notify_destruction(basic_iterator first, basic_iterator last, Func func) { + 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); + func(it, it + 1u); + } + } + + void swap_and_pop(basic_iterator first, basic_iterator last) final { + notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::swap_and_pop(args...); }); + } + + void in_place_pop(basic_iterator first, basic_iterator last) final { + notify_destruction(std::move(first), std::move(last), [this](auto... args) { Type::in_place_pop(args...); }); + } + + basic_iterator try_emplace(const typename 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 Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + + /*! @brief Inherited constructors. */ + using Type::Type; + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() ENTT_NOEXCEPT { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() ENTT_NOEXCEPT { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() ENTT_NOEXCEPT { + return sink{destruction}; + } + + /** + * @brief Assigns entities to a storage. + * @tparam Args Types of arguments to use to construct the object. + * @param 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 mixins, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) ENTT_NOEXCEPT final { + auto *reg = any_cast>(&value); + owner = reg ? reg : owner; + Type::bind(std::move(value)); + } + +private: + sigh &, const entity_type)> construction{}; + sigh &, const entity_type)> destruction{}; + sigh &, const entity_type)> update{}; + basic_registry *owner{}; +}; + +} // 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 "registry.hpp" + + +namespace entt { + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot { + using 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: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const basic_registry &source) ENTT_NOEXCEPT + : reg{&source} {} + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) ENTT_NOEXCEPT = default; + + /*! @brief Default move assignment operator. @return This snapshot. */ + basic_snapshot &operator=(basic_snapshot &&) ENTT_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 basic_registry *reg; +}; + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_snapshot_loader { + using 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 { + Type instance; + + while(length--) { + archive(entt, instance); + const auto entity = reg->valid(entt) ? entt : reg->create(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + reg->template emplace(entt, std::move(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->empty(), "Registry must be empty"); + } + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) ENTT_NOEXCEPT = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_snapshot_loader &operator=(basic_snapshot_loader &&) ENTT_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: + basic_registry *reg; +}; + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server applications with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + */ +template +class basic_continuous_loader { + using entity_traits = entt_traits; + + void destroy(Entity entt) { + if(const auto it = remloc.find(entt); it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + reg->destroy(local); + } + } + + void restore(Entity entt) { + const auto it = remloc.find(entt); + + if(it == remloc.cend()) { + const auto local = reg->create(); + remloc.emplace(entt, std::make_pair(local, true)); + } else { + if(!reg->valid(remloc[entt].first)) { + remloc[entt].first = reg->create(); + } + + // set the dirty flag + remloc[entt].second = true; + } + } + + template + auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) { + // map like container + Container other; + + for(auto &&pair: container) { + using first_type = std::remove_const_t::first_type>; + using second_type = typename std::decay_t::second_type; + + if constexpr(std::is_same_v && std::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + 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]] Other &instance, [[maybe_unused]] Member Type::*member) { + if constexpr(!std::is_same_v) { + return; + } else if constexpr(std::is_same_v) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + + template + void remove_if_exists() { + for(auto &&ref: remloc) { + const auto local = ref.second.first; + + if(reg->valid(local)) { + reg->template remove(local); + } + } + } + + template + void assign(Archive &archive, [[maybe_unused]] Member Type::*...member) { + typename 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 { + Other instance; + + while(length--) { + archive(entt, instance); + (update(instance, member), ...); + restore(entt); + reg->template emplace_or_replace(map(entt), std::move(instance)); + } + } + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(basic_registry &source) ENTT_NOEXCEPT + : reg{&source} {} + + /*! @brief Default move constructor. */ + basic_continuous_loader(basic_continuous_loader &&) = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_continuous_loader &operator=(basic_continuous_loader &&) = default; + + /** + * @brief Restores entities that were in use during serialization. + * + * This function restores the entities that were in use during serialization + * and creates local counterparts for them if required. + * + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader &entities(Archive &archive) { + typename 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 Type Types of components to update with local counterparts. + * @tparam Member Types of members to update with their local counterparts. + * @param archive A valid reference to an input archive. + * @param member Members to update with their local counterparts. + * @return A non-const reference to this loader. + */ + template + basic_continuous_loader &component(Archive &archive, Member Type::*...member) { + (remove_if_exists(), ...); + (assign(archive, member...), ...); + return *this; + } + + /** + * @brief Helps to purge entities that no longer have a conterpart. + * + * Users should invoke this member function after restoring each snapshot, + * unless they know exactly what they are doing. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &shrink() { + auto it = remloc.begin(); + + while(it != remloc.cend()) { + const auto local = it->second.first; + bool &dirty = it->second.second; + + if(dirty) { + dirty = false; + ++it; + } else { + if(reg->valid(local)) { + reg->destroy(local); + } + + it = remloc.erase(it); + } + } + + return *this; + } + + /** + * @brief Destroys those entities that have no components. + * + * In case all the entities were serialized but only part of the components + * was saved, it could happen that some of the entities have no components + * once restored.
+ * This functions helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &orphans() { + reg->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 ENTT_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 ENTT_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; + basic_registry *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; + + sparse_set_iterator() ENTT_NOEXCEPT + : packed{}, + offset{} {} + + sparse_set_iterator(const Container &ref, const difference_type idx) ENTT_NOEXCEPT + : packed{std::addressof(ref)}, + offset{idx} {} + + sparse_set_iterator &operator++() ENTT_NOEXCEPT { + return --offset, *this; + } + + sparse_set_iterator operator++(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + sparse_set_iterator &operator--() ENTT_NOEXCEPT { + return ++offset, *this; + } + + sparse_set_iterator operator--(int) ENTT_NOEXCEPT { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + sparse_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + offset -= value; + return *this; + } + + sparse_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + sparse_set_iterator copy = *this; + return (copy += value); + } + + sparse_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + sparse_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return packed->data()[index() - value]; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return packed->data() + index(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() < rhs.index(); +} + +template +[[nodiscard]] bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) ENTT_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(entity_traits::to_version(elem) == entity_traits::to_version(tombstone), "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 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 swap_and_pop(basic_iterator first, basic_iterator last) { + for(; first != last; ++first) { + sparse_ref(packed.back()) = entity_traits::combine(static_cast(first.index()), entity_traits::to_integral(packed.back())); + const auto entt = std::exchange(packed[first.index()], packed.back()); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = tombstone, true), ""); + // lazy self-assignment guard + sparse_ref(entt) = null; + packed.pop_back(); + } + } + + /** + * @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 in_place_pop(basic_iterator first, basic_iterator last) { + for(; first != last; ++first) { + sparse_ref(*first) = null; + packed[first.index()] = std::exchange(free_list, entity_traits::combine(static_cast(first.index()), entity_traits::reserved)); + } + } + + /** + * @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 = Entity; + /*! @brief Underlying version type. */ + using version_type = typename entity_traits::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename packed_container_type::size_type; + /*! @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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.empty(); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const ENTT_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 ENTT_NOEXCEPT { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const ENTT_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 ENTT_NOEXCEPT { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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 ENTT_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. + */ + const void *get(const entity_type entt) const ENTT_NOEXCEPT { + return get_at(index(entt)); + } + + /*! @copydoc get */ + void *get(const entity_type entt) 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); + 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)); + (mode == deletion_policy::in_place) ? in_place_pop(it, it + 1u) : swap_and_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) { + (mode == deletion_policy::in_place) ? in_place_pop(first, last) : swap_and_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) { + in_place_pop(begin(), last); + } else { + for(auto &&entity: *this) { + // tombstone filter on itself + if(const auto it = find(entity); it != last) { + in_place_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 ENTT_NOEXCEPT { + return *info; + } + + /*! @brief Forwards variables to mixins, if any. */ + virtual void bind(any) ENTT_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 "sigh_storage_mixin.hpp" + +// #include "sparse_set.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; + + storage_iterator() ENTT_NOEXCEPT = default; + + storage_iterator(Container *ref, difference_type idx) ENTT_NOEXCEPT + : packed{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + storage_iterator(const storage_iterator> &other) ENTT_NOEXCEPT + : packed{other.packed}, + offset{other.offset} {} + + storage_iterator &operator++() ENTT_NOEXCEPT { + return --offset, *this; + } + + storage_iterator operator++(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return ++(*this), orig; + } + + storage_iterator &operator--() ENTT_NOEXCEPT { + return ++offset, *this; + } + + storage_iterator operator--(int) ENTT_NOEXCEPT { + storage_iterator orig = *this; + return operator--(), orig; + } + + storage_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + offset -= value; + return *this; + } + + storage_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + storage_iterator copy = *this; + return (copy += value); + } + + storage_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + storage_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + const auto pos = index() - value; + return (*packed)[pos / comp_traits::page_size][fast_mod(pos, comp_traits::page_size)]; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + const auto pos = index(); + return (*packed)[pos / comp_traits::page_size] + fast_mod(pos, comp_traits::page_size); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] difference_type index() const ENTT_NOEXCEPT { + return offset - 1; + } + +private: + Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() < rhs.index(); +} + +template +[[nodiscard]] bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) ENTT_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; + + extended_storage_iterator() = default; + + extended_storage_iterator(It base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + extended_storage_iterator &operator++() ENTT_NOEXCEPT { + return ++std::get(it), (++std::get(it), ...), *this; + } + + extended_storage_iterator operator++(int) ENTT_NOEXCEPT { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {*std::get(it), *std::get(it)...}; + } + + template + friend bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) ENTT_NOEXCEPT; + +private: + std::tuple it; +}; + +template +[[nodiscard]] bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_NOEXCEPT { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) ENTT_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 Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects assigned to the entities. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + static_assert(std::is_move_constructible_v && std::is_move_assignable_v, "The type must be at least move constructible/assignable"); + + 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; + + [[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 { + if constexpr(comp_traits::in_place_delete) { + base_type::in_place_pop(it, it + 1u); + } else { + base_type::swap_and_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(const std::size_t lhs, const std::size_t rhs) final { + using std::swap; + swap(element_at(lhs), element_at(rhs)); + } + + void move_element(const std::size_t from, const std::size_t to) final { + 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 Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ + void swap_and_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + for(; first != last; ++first) { + auto &elem = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(element_at(static_cast(first.index())), std::move(elem)); + std::destroy_at(std::addressof(elem)); + base_type::swap_and_pop(first, first + 1u); + } + } + + /** + * @brief Erases elements from a storage. + * @param first An iterator to the first element to erase. + * @param last An iterator past the last element to erase. + */ + void in_place_pop(typename underlying_type::basic_iterator first, typename underlying_type::basic_iterator last) override { + for(; first != last; ++first) { + base_type::in_place_pop(first, first + 1u); + std::destroy_at(std::addressof(element_at(static_cast(first.index())))); + } + } + + /** + * @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. + */ + typename underlying_type::basic_iterator try_emplace([[maybe_unused]] const Entity entt, 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) ENTT_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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() ENTT_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 ENTT_NOEXCEPT { + const auto pos = static_cast(base_type::size()); + return const_iterator{&packed.first(), pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return const_iterator{&packed.first(), {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_NOEXCEPT { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const ENTT_NOEXCEPT { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() ENTT_NOEXCEPT { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * + * The returned iterator points to the element following the last instance + * of the reversed internal array. Attempting to dereference the returned + * iterator results in undefined behavior. + * + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const ENTT_NOEXCEPT { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const ENTT_NOEXCEPT { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() ENTT_NOEXCEPT { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const ENTT_NOEXCEPT { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) 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 ENTT_NOEXCEPT { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) 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() ENTT_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 ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const ENTT_NOEXCEPT { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } +}; + +/** + * @brief Provides a common way to access certain properties of storage types. + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Type Type of objects managed by the storage class. + */ +template +struct storage_traits { + /*! @brief Resulting type after component-to-storage conversion. */ + using storage_type = sigh_storage_mixin>; +}; + +} // namespace entt + +#endif + +// #include "entity/utility.hpp" +#ifndef ENTT_ENTITY_UTILITY_HPP +#define ENTT_ENTITY_UTILITY_HPP + +// #include "../core/type_traits.hpp" + + +namespace entt { + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t: type_list {}; + +/** + * @brief Variable template for exclusion lists. + * @tparam Type List of types. + */ +template +inline constexpr exclude_t exclude{}; + +/** + * @brief Alias for lists of observed components. + * @tparam Type List of types. + */ +template +struct get_t: type_list {}; + +/** + * @brief Variable template for lists of observed components. + * @tparam Type List of types. + */ +template +inline constexpr get_t get{}; + +/** + * @brief Alias for lists of owned components. + * @tparam Type List of types. + */ +template +struct owned_t: type_list {}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; + +} // namespace entt + +#endif + +// #include "entity/view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "utility.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid() const ENTT_NOEXCEPT { + return ((Component != 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; + + view_iterator() ENTT_NOEXCEPT = default; + + view_iterator(iterator_type curr, iterator_type to, std::array all_of, std::array none_of) ENTT_NOEXCEPT + : it{curr}, + last{to}, + pools{all_of}, + filter{none_of} { + if(it != last && !valid()) { + ++(*this); + } + } + + view_iterator &operator++() ENTT_NOEXCEPT { + while(++it != last && !valid()) {} + return *this; + } + + view_iterator operator++(int) ENTT_NOEXCEPT { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return &*it; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + template + friend bool operator==(const view_iterator &, const view_iterator &) ENTT_NOEXCEPT; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; +}; + +template +[[nodiscard]] bool operator==(const view_iterator &lhs, const view_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const view_iterator &lhs, const view_iterator &rhs) ENTT_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; + + extended_view_iterator() = default; + + extended_view_iterator(It from, std::tuple storage) + : it{from}, + pools{storage} {} + + extended_view_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + extended_view_iterator operator++(int) ENTT_NOEXCEPT { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_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 ENTT_NOEXCEPT { + return operator*(); + } + + template + friend bool operator==(const extended_view_iterator &, const extended_view_iterator &) ENTT_NOEXCEPT; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) ENTT_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 have at least all the + * given components in their bags. During initialization, a multi component view + * looks at the number of entities available for each component and uses the + * smallest set in order to get a performance boost when iterate. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +class basic_view, exclude_t> { + template + friend class basic_view; + + template + using storage_type = constness_as_t>::storage_type, Comp>; + + template + [[nodiscard]] auto pools_to_array(std::index_sequence) const ENTT_NOEXCEPT { + std::size_t pos{}; + std::array other{}; + (static_cast(std::get(pools) == view ? void() : void(other[pos++] = std::get(pools))), ...); + return other; + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Comp == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return std::get(pools)->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func func, std::index_sequence) const { + for(const auto curr: std::get(pools)->each()) { + const auto entt = std::get<0>(curr); + + if(((sizeof...(Component) != 1u) || (entt != tombstone)) + && ((Comp == Index || std::get(pools)->contains(entt)) && ...) + && std::apply([entt](const auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + 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 { + ((std::get(pools) == view ? each(std::move(func), seq) : void()), ...); + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = std::common_type_t::base_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() ENTT_NOEXCEPT + : pools{}, + filter{}, + view{} {} + + /** + * @brief Constructs a multi-type view from a set of storage classes. + * @param component The storage for the types to iterate. + * @param epool The storage for the types used to filter the view. + */ + basic_view(storage_type &...component, const storage_type &...epool) ENTT_NOEXCEPT + : pools{&component...}, + filter{&epool...}, + view{(std::min)({&static_cast(component)...}, [](auto *lhs, auto *rhs) { return lhs->size() < rhs->size(); })} {} + + /** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp 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 ENTT_NOEXCEPT { + basic_view other{*this}; + other.view = std::get *>(pools); + return other; + } + + /** + * @brief Creates a new view driven by a given component in its iterations. + * @tparam Comp 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 ENTT_NOEXCEPT { + basic_view other{*this}; + other.view = std::get(pools); + return other; + } + + /** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ + const base_type &handle() const ENTT_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 ENTT_NOEXCEPT { + return *std::get *>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_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 ENTT_NOEXCEPT { + return view->size(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * The returned iterator points to the first entity of the view. If the view + * is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return iterator{view->begin(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return iterator{view->end(), view->end(), pools_to_array(std::index_sequence_for{}), filter}; + } + + /** + * @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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return contains(entt) ? iterator{view->find(entt), view->end(), pools_to_array(std::index_sequence_for{}), filter} : 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 ENTT_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 ENTT_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 Comp 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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, pools); + } else if constexpr(sizeof...(Comp) == 1) { + return (std::get *>(pools)->get(entt), ...); + } else { + return std::tuple_cat(std::get *>(pools)->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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Other) == 0) { + return std::get(pools)->get(entt); + } else { + return std::tuple_cat(std::get(pools)->get_as_tuple(entt), 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 + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + pick_and_each(std::move(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 ENTT_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 Get Component list of the view to combine with. + * @tparam Excl 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 ENTT_NOEXCEPT { + using view_type = basic_view, exclude_t>; + return std::make_from_tuple(std::tuple_cat( + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, pools), + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, filter), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + } + +private: + std::tuple *...> pools; + std::array 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 instances of the given component are created and assigned to entities. + * * The entity currently pointed is modified (as an example, the given + * component is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pool iterated by the view in any way + * invalidates all the iterators and using them results in undefined behavior. + * + * @tparam Entity A valid entity type (see entt_traits for more details). + * @tparam Component Type of component iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>::in_place_delete>>> { + template + friend class basic_view; + + using storage_type = constness_as_t>::storage_type, Component>; + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using base_type = typename storage_type::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() ENTT_NOEXCEPT + : pools{}, + filter{}, + view{} {} + + /** + * @brief Constructs a single-type view from a storage class. + * @param ref The storage for the type to iterate. + */ + basic_view(storage_type &ref) ENTT_NOEXCEPT + : pools{&ref}, + filter{}, + view{&ref} {} + + /** + * @brief Returns the leading storage of a view. + * @return The leading storage of the view. + */ + const base_type &handle() const ENTT_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 ENTT_NOEXCEPT { + static_assert(std::is_same_v, "Invalid component type"); + return *std::get<0>(pools); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Comp Index of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] decltype(auto) storage() const ENTT_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 ENTT_NOEXCEPT { + return view->size(); + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return view->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 ENTT_NOEXCEPT { + return view->begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * + * The returned iterator points to the entity following the last entity of + * the view. Attempting to dereference the returned iterator results in + * undefined behavior. + * + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return view->end(); + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * The returned iterator points to the first entity of the reversed view. If + * the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const ENTT_NOEXCEPT { + return view->rbegin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * + * The returned iterator points to the entity following the last entity of + * the reversed view. Attempting to dereference the returned iterator + * results in undefined behavior. + * + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const ENTT_NOEXCEPT { + return view->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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return contains(entt) ? view->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 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 ENTT_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 ENTT_NOEXCEPT { + return view->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 Comp 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 { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + + if constexpr(sizeof...(Comp) == 0) { + return std::get<0>(pools)->get_as_tuple(entt); + } else { + static_assert(std::is_same_v, "Invalid component type"); + return std::get<0>(pools)->get(entt); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + ENTT_ASSERT(contains(entt), "View does not contain entity"); + return std::get<0>(pools)->get(entt); + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a reference to the component if it's a non-empty one. + * The _constness_ of the component is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Component &); + * void(Component &); + * @endcode + * + * @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(std::is_invocable_v) { + for(auto &&component: *std::get<0>(pools)) { + func(component); + } + } else if constexpr(std::is_invocable_v) { + for(auto entity: *view) { + func(entity); + } + } else { + for(size_type pos{}, last = size(); pos < last; ++pos) { + func(); + } + } + } + + /** + * @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 ENTT_NOEXCEPT { + return std::get<0>(pools)->each(); + } + + /** + * @brief Combines two views in a _more specific_ one (friend function). + * @tparam Get Component list of the view to combine with. + * @tparam Excl 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 ENTT_NOEXCEPT { + using view_type = basic_view, exclude_t>; + return std::make_from_tuple(std::tuple_cat( + std::forward_as_tuple(*std::get<0>(pools)), + std::apply([](auto *...curr) { return std::forward_as_tuple(*curr...); }, other.pools), + std::apply([](const auto *...curr) { return std::forward_as_tuple(static_cast &>(*curr)...); }, other.filter))); + } + +private: + std::tuple pools; + std::array filter; + const base_type *view; +}; + +/** + * @brief Deduction guide. + * @tparam Storage Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Storage &...storage) -> basic_view, get_t...>, exclude_t<>>; + +} // 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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 +struct locator final { + /*! @brief Service type. */ + using type = Service; + + /*! @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() ENTT_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() ENTT_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 Resets a service. */ + static void reset() ENTT_NOEXCEPT { + service.reset(); + } + +private: + // std::shared_ptr because of its type erased allocator which is pretty useful here + inline static std::shared_ptr service = nullptr; +}; + +} // 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 "../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 +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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; + + dense_map_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_map_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_map_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_map_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + dense_map_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_map_iterator operator--(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_map_iterator copy = *this; + return (copy += value); + } + + dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->element.first, it->element.second}; + } + + template + friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_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; + + dense_map_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].next, *this; + } + + dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const allocator_type &allocator) + : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_map{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 + * 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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 +// #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; + + dense_set_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_set_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_set_iterator(const dense_set_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_set_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_set_iterator operator++(int) ENTT_NOEXCEPT { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + dense_set_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_set_iterator operator--(int) ENTT_NOEXCEPT { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + dense_set_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_set_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_set_iterator copy = *this; + return (copy += value); + } + + dense_set_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_set_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return it[value].second; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return std::addressof(it->second); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + template + friend std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_set_iterator &, const dense_set_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) ENTT_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; + + dense_set_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_set_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_set_local_iterator(const dense_set_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_set_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].first, *this; + } + + dense_set_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return std::addressof(it[offset].second); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return *operator->(); + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type bucket_count, const allocator_type &allocator) + : dense_set{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_set{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 "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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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" +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#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 "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + enum class operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get + }; + + enum class policy : std::uint8_t { + owner, + ref, + cref + }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] 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 *static_cast(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) { + if constexpr(!std::is_void_v) { + info = &type_id>>(); + 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) Type{std::forward(args)...}; + } else { + new(&storage) Type(std::forward(args)...); + } + } else { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + : instance{other.data()}, + 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() ENTT_NOEXCEPT + : instance{}, + info{&type_id()}, + vtable{}, + mode{policy::owner} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : basic_any{} { + 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{} { + initialize>(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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + } + + /** + * @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) ENTT_NOEXCEPT { + return *info == req ? data() : nullptr; + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + 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 any &other) { + if(vtable && mode != policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(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); + } + + 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 ENTT_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. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + return basic_any{*this, policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return (mode == policy::owner); + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + 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) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +Type *any_cast(basic_any *data) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + // last attempt to make wrappers for const references return their values + return static_cast(static_cast, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "ctx.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #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 + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct meta_type_node; + +struct ENTT_API meta_context { + // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ + // inline static meta_type_node *local = nullptr; + // inline static meta_type_node **global = &local; + + [[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { + static meta_type_node *chain = nullptr; + return chain; + } + + [[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { + static meta_type_node **chain = &local(); + return chain; + } +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_context::global() = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + +} // namespace entt + +#endif + +// #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 "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include +// #include "../config/config.h" + + +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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) ENTT_NOEXCEPT { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) ENTT_NOEXCEPT { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.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_array = 0x0008, + is_enum = 0x0010, + is_class = 0x0020, + is_pointer = 0x0040, + is_meta_pointer_like = 0x0080, + is_meta_sequence_container = 0x0100, + is_meta_associative_container = 0x0200, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_prop_node *next; + const meta_any &id; + meta_any &value; +}; + +struct meta_base_node { + meta_base_node *next; + meta_type_node *const type; + meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; +}; + +struct meta_conv_node { + meta_conv_node *next; + meta_type_node *const type; + meta_any (*const conv)(const meta_any &); +}; + +struct meta_ctor_node { + using size_type = std::size_t; + meta_ctor_node *next; + const size_type arity; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + meta_any (*const invoke)(meta_any *const); +}; + +struct meta_data_node { + using size_type = std::size_t; + id_type id; + const meta_traits traits; + meta_data_node *next; + meta_prop_node *prop; + const size_type arity; + meta_type_node *const type; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + bool (*const set)(meta_handle, meta_any); + meta_any (*const get)(meta_handle); +}; + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + const meta_traits traits; + meta_func_node *next; + meta_prop_node *prop; + const size_type arity; + meta_type_node *const ret; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + meta_any (*const invoke)(meta_handle, meta_any *const); +}; + +struct meta_template_node { + using size_type = std::size_t; + const size_type arity; + meta_type_node *const type; + meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; +}; + +struct meta_type_node { + using size_type = std::size_t; + const type_info *info; + id_type id; + const meta_traits traits; + meta_type_node *next; + meta_prop_node *prop; + const size_type size_of; + meta_any (*const default_constructor)(); + double (*const conversion_helper)(void *, const void *); + const meta_template_node *const templ; + meta_ctor_node *ctor{nullptr}; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; + void (*dtor)(void *){nullptr}; +}; + +template +meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + +template +class ENTT_API meta_node { + static_assert(std::is_same_v>>, "Invalid type"); + + [[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { + if constexpr(std::is_default_constructible_v) { + return +[]() { return meta_any{std::in_place_type}; }; + } else { + return static_cast>(nullptr); + } + } + + [[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { + if constexpr(std::is_arithmetic_v) { + return +[](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) { + return +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } else { + return static_cast>(nullptr); + } + } + + [[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { + if constexpr(is_complete_v>) { + static meta_template_node node{ + meta_template_traits::args_type::size, + meta_node::class_type>::resolve(), + [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } + // tricks clang-format + }; + + return &node; + } else { + return nullptr; + } + } + +public: + [[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + &type_id(), + {}, + internal::meta_traits::is_none + | (std::is_arithmetic_v ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) + | (std::is_array_v ? internal::meta_traits::is_array : internal::meta_traits::is_none) + | (std::is_enum_v ? internal::meta_traits::is_enum : internal::meta_traits::is_none) + | (std::is_class_v ? internal::meta_traits::is_class : internal::meta_traits::is_none) + | (std::is_pointer_v ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) + | (is_meta_pointer_like_v ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) + | (is_complete_v> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) + | (is_complete_v> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), + nullptr, + nullptr, + size_of_v, + meta_default_constructor(), + meta_conversion_helper(), + meta_template_info() + // tricks clang-format + }; + + return &node; + } +}; + +template +[[nodiscard]] meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { + meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node>>::resolve()...}; + return args[index + 1u]; +} + +template +[[nodiscard]] static std::decay_t().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { + for(auto *curr = node->*Member; curr; curr = curr->next) { + if constexpr(std::is_same_v) { + if(*curr->type->info == info_or_id) { + return curr; + } + } else if constexpr(std::is_same_v) { + if(curr->type->id == info_or_id) { + return curr; + } + } else { + if(curr->id == info_or_id) { + return curr; + } + } + } + + for(auto *curr = node->base; curr; curr = curr->next) { + if(auto *ret = find_by(info_or_id, curr->type); ret) { + return ret; + } + } + + return nullptr; +} + +} // 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 "../core/iterator.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 = Type; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using node_type = Node; + + meta_range_iterator() ENTT_NOEXCEPT + : it{} {} + + meta_range_iterator(node_type *head) ENTT_NOEXCEPT + : it{head} {} + + meta_range_iterator &operator++() ENTT_NOEXCEPT { + return (it = it->next), *this; + } + + meta_range_iterator operator++(int) ENTT_NOEXCEPT { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return it; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { + return it == other.it; + } + + [[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + node_type *it; +}; + +} // 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 Node Type of meta nodes iterated. + */ +template +struct meta_range final { + /*! @brief Node type. */ + using node_type = Node; + /*! @brief Input iterator type. */ + using iterator = internal::meta_range_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = iterator; + + /*! @brief Default constructor. */ + meta_range() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ + meta_range(node_type *head) ENTT_NOEXCEPT + : node{head} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ + [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + return iterator{node}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ + [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + return iterator{}; + } + + /*! @copydoc cend */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return cend(); + } + +private: + node_type *node{nullptr}; +}; + +} // 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 Default constructor. */ + meta_sequence_container() ENTT_NOEXCEPT = default; + + /** + * @brief Construct a proxy object for sequence containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : value_type_node{internal::meta_node>>::resolve()}, + size_fn{&meta_sequence_container_traits::size}, + resize_fn{&meta_sequence_container_traits::resize}, + iter_fn{&meta_sequence_container_traits::iter}, + insert_fn{&meta_sequence_container_traits::insert}, + erase_fn{&meta_sequence_container_traits::erase}, + storage{std::move(instance)} {} + + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_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 ENTT_NOEXCEPT; + +private: + internal::meta_type_node *value_type_node = nullptr; + size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool (*resize_fn)(any &, size_type) = nullptr; + iterator (*iter_fn)(any &, const bool) = nullptr; + iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; + iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; + 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 Default constructor. */ + meta_associative_container() ENTT_NOEXCEPT = default; + + /** + * @brief Construct a proxy object for associative containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : key_only_container{meta_associative_container_traits::key_only}, + key_type_node{internal::meta_node>>::resolve()}, + mapped_type_node{nullptr}, + value_type_node{internal::meta_node>>::resolve()}, + size_fn{&meta_associative_container_traits::size}, + clear_fn{&meta_associative_container_traits::clear}, + iter_fn{&meta_associative_container_traits::iter}, + insert_fn{&meta_associative_container_traits::insert}, + erase_fn{&meta_associative_container_traits::erase}, + find_fn{&meta_associative_container_traits::find}, + storage{std::move(instance)} { + if constexpr(!meta_associative_container_traits::key_only) { + mapped_type_node = internal::meta_node>>::resolve(); + } + } + + [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + inline bool clear(); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline bool erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: + bool key_only_container{}; + internal::meta_type_node *key_type_node = nullptr; + internal::meta_type_node *mapped_type_node = nullptr; + internal::meta_type_node *value_type_node = nullptr; + size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool (*clear_fn)(any &) = nullptr; + iterator (*iter_fn)(any &, const bool) = nullptr; + bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; + bool (*erase_fn)(any &, meta_any &) = nullptr; + iterator (*find_fn)(any &, meta_any &) = nullptr; + any storage{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + enum class operation : 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) = 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) = {std::in_place_type, std::move(const_cast(value))}; + } + break; + case operation::assoc: + if constexpr(is_complete_v>) { + *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + } + break; + } + } + } + + void release() { + if(node && node->dtor && storage.owner()) { + node->dtor(storage.data()); + } + } + + meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT + : storage{std::move(ref)}, + node{storage ? other.node : nullptr}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + node{internal::meta_node>>::resolve()}, + 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{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) ENTT_NOEXCEPT + : storage{std::move(other.storage)}, + node{std::exchange(other.node, nullptr)}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + 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(); + vtable = other.vtable; + storage = other.storage; + node = other.node; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { + release(); + vtable = std::exchange(other.vtable, &basic_vtable); + storage = std::move(other.storage); + node = std::exchange(other.node, nullptr); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + 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 ENTT_NOEXCEPT; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * + * @sa meta_func::invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + [[nodiscard]] const Type *try_cast() const { + if(const auto &info = type_id(); node && *node->info == info) { + return any_cast(&storage); + } else if(node) { + for(auto *it = node->base; it; it = it->next) { + const auto as_const = it->cast(as_ref()); + + if(const Type *base = as_const.template try_cast(); base) { + return base; + } + } + } + + return nullptr; + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if(const auto &info = type_id(); node && *node->info == info) { + return any_cast(&storage); + } else if(node) { + for(auto *it = node->base; it; it = it->next) { + if(Type *base = it->cast(as_ref()).template try_cast(); base) { + return base; + } + } + } + + return nullptr; + } + + /** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform an invalid cast results 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.storage.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 { + const auto other = allow_cast(internal::meta_node>>::resolve()); + + if constexpr(std::is_reference_v && !std::is_const_v>) { + return other.storage.owner() ? other : meta_any{}; + } else { + return 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() { + if(auto other = std::as_const(*this).allow_cast(internal::meta_node>>::resolve()); other) { + if(other.storage.owner()) { + std::swap(*this, other); + return true; + } + + return (static_cast> &>(storage).data() != nullptr); + } + + return false; + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + vtable = &basic_vtable>>; + storage.emplace(std::forward(args)...); + node = internal::meta_node>>::resolve(); + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + vtable = &basic_vtable; + storage.reset(); + node = nullptr; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_sequence_container proxy; + vtable(operation::seq, detached, &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_sequence_container proxy; + 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() ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_associative_container proxy; + vtable(operation::assoc, detached, &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_associative_container proxy; + 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 ENTT_NOEXCEPT { + meta_any ret{}; + vtable(operation::deref, storage, &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const { + return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return storage.owner(); + } + +private: + any storage; + internal::meta_type_node *node; + vtable_type *vtable; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template +meta_any make_meta(Args &&...args) { + return meta_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +meta_any forward_as_meta(Type &&value) { + return meta_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.
+ * Handles are used to generate references to actual objects when needed. + */ +struct meta_handle { + /*! @brief Default constructor. */ + meta_handle() = default; + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) ENTT_NOEXCEPT + : meta_handle{} { + if constexpr(std::is_same_v, meta_any>) { + any = value.as_ref(); + } else { + any.emplace(value); + } + } + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(any); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Node type. */ + using node_type = internal::meta_prop_node; + + /** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /** + * @brief Returns the stored key as a const reference. + * @return A wrapper containing the key stored with the property. + */ + [[nodiscard]] meta_any key() const { + return node->id.as_ref(); + } + + /** + * @brief Returns the stored value by copy. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Node type. */ + using node_type = internal::meta_data_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const ENTT_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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.
+ * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(std::move(instance)); + } + + /** + * @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 ENTT_NOEXCEPT; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for registered 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(meta_any key) const { + for(auto curr: prop()) { + if(curr.key() == key) { + return curr; + } + } + + return nullptr; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Node type. */ + using node_type = internal::meta_func_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Invokes the underlying function, if possible. + * + * To invoke a member function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.
+ * It must be possible to cast the instance to the parent type of the member + * function. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for registered 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(meta_any key) const { + for(auto curr: prop()) { + if(curr.key() == key) { + return curr; + } + } + + return nullptr; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] std::decay_t().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { + std::decay_t*Member)> candidate{}; + size_type extent{sz + 1u}; + bool ambiguous{}; + + for(auto *curr = (node->*Member); curr; curr = curr->next) { + if(pred(curr) && curr->arity == sz) { + size_type direct{}; + size_type ext{}; + + for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { + const auto type = args[next].type(); + const auto other = curr->arg(next); + + if(const auto &info = other.info(); info == type.info()) { + ++direct; + } else { + ext += internal::find_by<&node_type::base>(info, type.node) + || internal::find_by<&node_type::conv>(info, type.node) + || (type.node->conversion_helper && other.node->conversion_helper); + } + } + + if((direct + ext) == sz) { + if(ext < extent) { + candidate = curr; + extent = ext; + ambiguous = false; + } else if(ext == extent) { + ambiguous = true; + } + } + } + } + + return (candidate && !ambiguous) ? candidate : decltype(candidate){}; + } + +public: + /*! @brief Node type. */ + using node_type = internal::meta_type_node; + /*! @brief Node type. */ + using base_node_type = internal::meta_base_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /** + * @brief Constructs an instance from a given base node. + * @param curr The base node with which to construct the instance. + */ + meta_type(const base_node_type *curr) ENTT_NOEXCEPT + : node{curr ? curr->type : nullptr} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { + return *node->info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { + return node->size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_pointer); + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is a pointer-like one, false + * otherwise. + */ + [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return (node->templ != nullptr); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { + return node->templ ? node->templ->arity : size_type{}; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * + * @sa meta_class_template_tag + * + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { + return node->templ ? node->templ->type : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { + return index < template_arity() ? node->templ->arg(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 ENTT_NOEXCEPT { + return node->base; + } + + /** + * @brief Lookup function for registered base meta types. + * @param id Unique identifier. + * @return The registered base meta type for the given identifier, if any. + */ + [[nodiscard]] meta_type base(const id_type id) const { + return internal::find_by<&node_type::base>(id, node); + } + + /** + * @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 ENTT_NOEXCEPT { + return node->data; + } + + /** + * @brief Lookup function for registered meta data. + * + * Registered meta data of base classes will also be 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 { + return internal::find_by<&node_type::data>(id, node); + } + + /** + * @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 ENTT_NOEXCEPT { + return node->func; + } + + /** + * @brief Lookup function for registered meta functions. + * + * Registered meta functions of base classes will also be visited.
+ * In case of overloaded functions, the first one with the required + * identifier will be 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 { + return internal::find_by<&node_type::func>(id, node); + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters are such that a cast or conversion to the required types is + * possible. Otherwise, an empty and thus invalid wrapper is returned.
+ * If suitable, the implicitly generated default constructor is used. + * + * @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 { + const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); + return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); + } + + /** + * @copybrief construct + * + * @sa construct + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @sa meta_func::invoke + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + + for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { + candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + } + + return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.
+ * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for meta properties. + * + * Properties of base classes 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(meta_any key) const { + return internal::find_by<&internal::meta_type_node::prop>(key, node); + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT { + return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); + } + +private: + const node_type *node; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { + return node; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + if(const auto &info = type.info(); node && *node->info == info) { + return as_ref(); + } else if(node) { + for(auto *it = node->conv; it; it = it->next) { + if(*it->type->info == info) { + return it->conv(*this); + } + } + + 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; + } + + for(auto *it = node->base; it; it = it->next) { + const auto as_const = it->cast(as_ref()); + + if(auto other = as_const.allow_cast(type); other) { + return other; + } + } + } + + return {}; +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast(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 ENTT_NOEXCEPT { + return node->type; +} + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(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; + + using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + + template + static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { + return meta_any{std::in_place_type::reference>, any_cast(value)[pos]}; + } + +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; + + meta_iterator() ENTT_NOEXCEPT + : deref{}, + offset{}, + handle{} {} + + template + explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT + : deref{&deref_fn}, + offset{init}, + handle{std::move(iter)} {} + + meta_iterator &operator++() ENTT_NOEXCEPT { + return ++offset, *this; + } + + meta_iterator operator++(int value) ENTT_NOEXCEPT { + meta_iterator orig = *this; + offset += ++value; + return orig; + } + + meta_iterator &operator--() ENTT_NOEXCEPT { + return --offset, *this; + } + + meta_iterator operator--(int value) ENTT_NOEXCEPT { + meta_iterator orig = *this; + offset -= ++value; + return orig; + } + + [[nodiscard]] reference operator*() const { + return deref(handle, offset); + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return offset == other.offset; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + deref_fn_type *deref; + difference_type offset; + 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; + + meta_iterator() ENTT_NOEXCEPT + : vtable{}, + handle{} {} + + template + meta_iterator(std::integral_constant, It iter) ENTT_NOEXCEPT + : vtable{&basic_vtable}, + handle{std::move(iter)} {} + + meta_iterator &operator++() ENTT_NOEXCEPT { + vtable(operation::incr, handle, nullptr); + return *this; + } + + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const { + reference other; + vtable(operation::deref, handle, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + 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 ENTT_NOEXCEPT { + return value_type_node; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { + return size_fn(storage); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(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(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(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_fn(storage, it.offset, 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 erase_fn(storage, it.offset); +} + +/** + * @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 ENTT_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 ENTT_NOEXCEPT { + return key_only_container; +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { + return key_type_node; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { + return mapped_type_node; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { + return value_type_node; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { + return size_fn(storage); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return clear_fn(storage); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return iter_fn(storage, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return iter_fn(storage, true); +} + +/** + * @brief Inserts an element (a key/value pair) into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return insert_fn(storage, key, value); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline bool meta_associative_container::erase(meta_any key) { + return erase_fn(storage, key); +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return find_fn(storage, key); +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { + return static_cast(storage); +} + +} // 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) ENTT_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(any &container, const bool as_end) { + using std::begin; + + if(auto *const cont = any_cast(&container); cont) { + return iterator{begin(*cont), static_cast(as_end * cont->size())}; + } + + const Type &as_const = any_cast(container); + return iterator{begin(as_const), static_cast(as_end * as_const.size())}; + } + + [[nodiscard]] static iterator insert([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset, [[maybe_unused]] meta_any &value) { + if constexpr(is_dynamic_sequence_container::value) { + if(auto *const cont = any_cast(&container); cont) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(value.allow_cast() || value.allow_cast()) { + using std::begin; + const auto *element = value.try_cast>(); + const auto curr = cont->insert(begin(*cont) + offset, element ? *element : value.cast()); + return iterator{curr, curr - begin(*cont)}; + } + } + } + + return {}; + } + + [[nodiscard]] static iterator erase([[maybe_unused]] any &container, [[maybe_unused]] const std::ptrdiff_t offset) { + if constexpr(is_dynamic_sequence_container::value) { + if(auto *const cont = any_cast(&container); cont) { + using std::begin; + const auto curr = cont->erase(begin(*cont) + offset); + return iterator{curr, curr - begin(*cont)}; + } + } + + return {}; + } +}; + +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) ENTT_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(any &container, const bool as_end) { + using std::begin; + using std::end; + + if(auto *const cont = any_cast(&container); cont) { + return iterator{std::integral_constant{}, as_end ? end(*cont) : begin(*cont)}; + } + + const auto &as_const = any_cast(container); + return iterator{std::integral_constant{}, as_end ? end(as_const) : begin(as_const)}; + } + + [[nodiscard]] static bool insert(any &container, meta_any &key, [[maybe_unused]] meta_any &value) { + auto *const cont = any_cast(&container); + + if constexpr(is_key_only_meta_associative_container::value) { + return cont && key.allow_cast() + && cont->insert(key.cast()).second; + } else { + return cont && key.allow_cast() && value.allow_cast() + && cont->emplace(key.cast(), value.cast()).second; + } + } + + [[nodiscard]] static bool erase(any &container, meta_any &key) { + auto *const cont = any_cast(&container); + return cont && key.allow_cast() + && (cont->erase(key.cast()) != cont->size()); + } + + [[nodiscard]] static iterator find(any &container, meta_any &key) { + if(key.allow_cast()) { + if(auto *const cont = any_cast(&container); cont) { + return iterator{std::integral_constant{}, cont->find(key.cast())}; + } + + return iterator{std::integral_constant{}, any_cast(container).find(key.cast())}; + } + + return {}; + } +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Type The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type The type of elements. + * @tparam N The number of elements. + */ +template +struct meta_sequence_container_traits> + : internal::basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Key The key type of elements. + * @tparam Value The value type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Key The type of elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Key The key type of the elements. + * @tparam Type The value type of the elements. + * @tparam Args Other arguments. + */ +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 Type The value type of the elements. + * @tparam Args Other arguments. + */ +template +struct meta_associative_container_traits> + : internal::basic_meta_associative_container_traits> {}; + +} // namespace entt + +#endif + +// #include "meta/ctx.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../config/config.h" + +// #include "../core/attribute.h" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +struct meta_type_node; + +struct ENTT_API meta_context { + // we could use the lines below but VS2017 returns with an ICE if combined with ENTT_API despite the code being valid C++ + // inline static meta_type_node *local = nullptr; + // inline static meta_type_node **global = &local; + + [[nodiscard]] static meta_type_node *&local() ENTT_NOEXCEPT { + static meta_type_node *chain = nullptr; + return chain; + } + + [[nodiscard]] static meta_type_node **&global() ENTT_NOEXCEPT { + static meta_type_node **chain = &local(); + return chain; + } +}; + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Opaque container for a meta context. */ +struct meta_ctx { + /** + * @brief Binds the meta system to a given context. + * @param other A valid context to which to bind. + */ + static void bind(meta_ctx other) ENTT_NOEXCEPT { + internal::meta_context::global() = other.ctx; + } + +private: + internal::meta_type_node **ctx{&internal::meta_context::local()}; +}; + +} // 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 "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 { + /** + * @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 { + /** + * @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 { + /** + * @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 { + /** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + template + static constexpr bool value = true; + /** + * Internal details not to be documented. + * @endcond + */ +}; + +} // namespace entt + +#endif + +// #include "range.hpp" + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = true; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @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 { + /*! @brief Meta data return type. */ + using return_type = Ret &; + /*! @brief Meta data arguments. */ + using args_type = std::conditional_t, type_list<>, type_list>; + + /*! @brief True if the meta data is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta data is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @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 { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t>, Type>, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = std::is_base_of_v>, Type> && std::is_const_v>; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v>, Type>; +}; + +/** + * @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 { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = type_list<>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = true; +}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret 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. + * @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 +meta_any meta_dispatch([[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{std::in_place_type &>, std::as_const(value)}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { + return internal::meta_arg_node(Type{}, 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. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { + if constexpr(std::is_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(std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(std::invoke(Data, *fallback)); + } + } + } + + return meta_any{}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return meta_dispatch(*Data); + } + } else { + return meta_dispatch(Data); + } +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { + if constexpr(std::is_same_v, void>) { + std::invoke(candidate, args...); + return meta_any{std::in_place_type}; + } else { + return meta_dispatch(std::invoke(candidate, args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke([[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(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(std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{}; +} + +template +[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @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]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { + return internal::meta_invoke(std::move(instance), std::forward(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]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { + return internal::meta_invoke(std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @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 internal::meta_construct(args, std::index_sequence_for{}); +} + +/** + * @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 args Parameters to use to _invoke_ the object. + * @param candidate The actual object to _invoke_. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static) { + return internal::meta_invoke({}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(*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 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]] meta_any meta_construct(meta_any *const args) { + return meta_construct(Candidate, args); +} + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Meta factory to be used for reflection purposes. + * + * The meta factory is an utility class used to reflect types, data members and + * functions of all sorts. This class ensures that the underlying web of types + * is built correctly and performs some checks in debug mode to ensure that + * there are no subtle errors at runtime. + */ +template +class meta_factory; + +/** + * @brief Extended meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + * @tparam Spec Property specialization pack used to disambiguate overloads. + */ +template +class meta_factory: public meta_factory { + void link_prop_if_required(internal::meta_prop_node &node) ENTT_NOEXCEPT { + if(meta_range range{*ref}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [&node](const auto *curr) { return curr->id == node.id; }) == range.cend(), "Duplicate identifier"); + node.next = *ref; + *ref = &node; + } + } + + template + void unroll(choice_t<2>, std::tuple property, Other &&...other) ENTT_NOEXCEPT { + std::apply([this](auto &&...curr) { (this->unroll(choice<2>, std::forward(curr)...)); }, property); + unroll(choice<2>, std::forward(other)...); + } + + template + void unroll(choice_t<1>, std::pair property, Other &&...other) ENTT_NOEXCEPT { + assign(std::move(property.first), std::move(property.second)); + unroll(choice<2>, std::forward(other)...); + } + + template + void unroll(choice_t<0>, Property &&property, Other &&...other) ENTT_NOEXCEPT { + assign(std::forward(property)); + unroll(choice<2>, std::forward(other)...); + } + + template + void unroll(choice_t<0>) ENTT_NOEXCEPT {} + + template + void assign(meta_any key, meta_any value = {}) { + static meta_any property[2u]{}; + + static internal::meta_prop_node node{ + nullptr, + property[0u], + property[1u] + // tricks clang-format + }; + + property[0u] = std::move(key); + property[1u] = std::move(value); + + link_prop_if_required(node); + } + +public: + /** + * @brief Constructs an extended factory from a given node. + * @param target The underlying node to which to assign the properties. + */ + meta_factory(internal::meta_prop_node **target) ENTT_NOEXCEPT + : ref{target} {} + + /** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam PropertyOrKey Type of the property or property key. + * @tparam Value Optional type of the property value. + * @param property_or_key Property or property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template + meta_factory prop(PropertyOrKey &&property_or_key, Value &&...value) { + if constexpr(sizeof...(Value) == 0) { + unroll(choice<2>, std::forward(property_or_key)); + } else { + assign(std::forward(property_or_key), std::forward(value)...); + } + + return {}; + } + + /** + * @brief Assigns properties to the last meta object created. + * + * Both key and value (if any) must be at least copy constructible. + * + * @tparam Property Types of the properties. + * @param property Properties to assign to the last meta object created. + * @return A meta factory for the parent type. + */ + template + meta_factory props(Property... property) { + unroll(choice<2>, std::forward(property)...); + return {}; + } + +private: + internal::meta_prop_node **ref; +}; + +/** + * @brief Basic meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template +class meta_factory { + void link_base_if_required(internal::meta_base_node &node) ENTT_NOEXCEPT { + if(meta_range range{owner->base}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + node.next = owner->base; + owner->base = &node; + } + } + + void link_conv_if_required(internal::meta_conv_node &node) ENTT_NOEXCEPT { + if(meta_range range{owner->conv}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + node.next = owner->conv; + owner->conv = &node; + } + } + + void link_ctor_if_required(internal::meta_ctor_node &node) ENTT_NOEXCEPT { + if(meta_range range{owner->ctor}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + node.next = owner->ctor; + owner->ctor = &node; + } + } + + void link_data_if_required(const id_type id, internal::meta_data_node &node) ENTT_NOEXCEPT { + meta_range range{owner->data}; + ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, &node](const auto *curr) { return curr != &node && curr->id == id; }) == range.cend(), "Duplicate identifier"); + node.id = id; + + if(std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + node.next = owner->data; + owner->data = &node; + } + } + + void link_func_if_required(const id_type id, internal::meta_func_node &node) ENTT_NOEXCEPT { + node.id = id; + + if(meta_range range{owner->func}; std::find(range.cbegin(), range.cend(), &node) == range.cend()) { + node.next = owner->func; + owner->func = &node; + } + } + + template + auto data(const id_type id, std::index_sequence) ENTT_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"); + + static internal::meta_data_node 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, + nullptr, + nullptr, + Setter::size, + internal::meta_node>>::resolve(), + &meta_arg::size != 1u, type_list_element_t>...>>, + [](meta_handle instance, meta_any value) -> bool { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, + &meta_getter + // tricks clang-format + }; + + link_data_if_required(id, node); + return meta_factory>{&node.prop}; + } + +public: + /*! @brief Default constructor. */ + meta_factory() ENTT_NOEXCEPT + : owner{internal::meta_node::resolve()} {} + + /** + * @brief Makes a meta type _searchable_. + * @param id Optional unique identifier. + * @return An extended meta factory for the given type. + */ + auto type(const id_type id = type_hash::value()) ENTT_NOEXCEPT { + meta_range range{*internal::meta_context::global()}; + ENTT_ASSERT(std::find_if(range.cbegin(), range.cend(), [id, this](const auto *curr) { return curr != owner && curr->id == id; }) == range.cend(), "Duplicate identifier"); + owner->id = id; + + if(std::find(range.cbegin(), range.cend(), owner) == range.cend()) { + owner->next = *internal::meta_context::global(); + *internal::meta_context::global() = owner; + } + + return meta_factory{&owner->prop}; + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto base() ENTT_NOEXCEPT { + static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); + + static internal::meta_base_node node{ + nullptr, + internal::meta_node::resolve(), + [](meta_any other) ENTT_NOEXCEPT -> meta_any { + if(auto *ptr = other.data(); ptr) { + return forward_as_meta(*static_cast(static_cast(ptr))); + } + + return forward_as_meta(*static_cast(static_cast(std::as_const(other).data()))); + } + // tricks clang-format + }; + + link_base_if_required(node); + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.
+ * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + static internal::meta_conv_node node{ + nullptr, + internal::meta_node>>>::resolve(), + [](const meta_any &instance) -> meta_any { + return forward_as_meta(std::invoke(Candidate, *static_cast(instance.data()))); + } + // tricks clang-format + }; + + link_conv_if_required(node); + return meta_factory{}; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto conv() ENTT_NOEXCEPT { + static internal::meta_conv_node node{ + nullptr, + internal::meta_node>>::resolve(), + [](const meta_any &instance) -> meta_any { return forward_as_meta(static_cast(*static_cast(instance.data()))); } + // tricks clang-format + }; + + link_conv_if_required(node); + return meta_factory{}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function can be assigned to meta types in + * the role of constructors. All that is required is that they return an + * instance of the underlying type.
+ * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using descriptor = meta_function_helper_t; + static_assert(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"); + + static internal::meta_ctor_node node{ + nullptr, + descriptor::args_type::size, + &meta_arg, + &meta_construct + // tricks clang-format + }; + + link_ctor_if_required(node); + return meta_factory{}; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return An extended meta factory for the parent type. + */ + template + auto ctor() ENTT_NOEXCEPT { + using descriptor = meta_function_helper_t; + + static internal::meta_ctor_node node{ + nullptr, + descriptor::args_type::size, + &meta_arg, + &meta_construct + // tricks clang-format + }; + + link_ctor_if_required(node); + return meta_factory{}; + } + + /** + * @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() ENTT_NOEXCEPT { + static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); + owner->dtor = [](void *instance) { std::invoke(Func, *static_cast(instance)); }; + return meta_factory{}; + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.
+ * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t>; + + static internal::meta_data_node node{ + {}, + /* this is never static */ + std::is_const_v ? internal::meta_traits::is_const : internal::meta_traits::is_none, + nullptr, + nullptr, + 1u, + internal::meta_node>::resolve(), + &meta_arg>>, + &meta_setter, + &meta_getter + // tricks clang-format + }; + + link_data_if_required(id, node); + return meta_factory, std::integral_constant>{&node.prop}; + } else { + using data_type = std::remove_reference_t>; + + static internal::meta_data_node node{ + {}, + ((std::is_same_v> || std::is_const_v) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + nullptr, + nullptr, + 1u, + internal::meta_node>::resolve(), + &meta_arg>>, + &meta_setter, + &meta_getter + // tricks clang-format + }; + + link_data_if_required(id, node); + return meta_factory>{&node.prop}; + } + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.
+ * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.
+ * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return An extended meta factory for the parent type. + */ + template + auto data(const id_type id) ENTT_NOEXCEPT { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + if constexpr(std::is_same_v) { + static internal::meta_data_node node{ + {}, + /* this is never static */ + internal::meta_traits::is_const, + nullptr, + nullptr, + 0u, + internal::meta_node>>::resolve(), + &meta_arg>, + &meta_setter, + &meta_getter + // tricks clang-format + }; + + link_data_if_required(id, node); + return meta_factory, std::integral_constant>{&node.prop}; + } else { + using args_type = typename meta_function_helper_t::args_type; + + static internal::meta_data_node node{ + {}, + /* this is never static nor const */ + internal::meta_traits::is_none, + nullptr, + nullptr, + 1u, + internal::meta_node>>::resolve(), + &meta_arg>>, + &meta_setter, + &meta_getter + // tricks clang-format + }; + + link_data_if_required(id, node); + return meta_factory, std::integral_constant>{&node.prop}; + } + } + + /** + * @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) ENTT_NOEXCEPT { + return data(id, std::make_index_sequence{}); + } + + /** + * @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) ENTT_NOEXCEPT { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + static internal::meta_func_node 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), + nullptr, + nullptr, + descriptor::args_type::size, + internal::meta_node, void, std::remove_cv_t>>>::resolve(), + &meta_arg, + &meta_invoke + // tricks clang-format + }; + + link_func_if_required(id, node); + return meta_factory>{&node.prop}; + } + +private: + internal::meta_type_node *owner; +}; + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta() ENTT_NOEXCEPT { + auto *const node = internal::meta_node::resolve(); + // extended meta factory to allow assigning properties to opaque meta types + return meta_factory{&node->prop}; +} + +/** + * @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 list of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) ENTT_NOEXCEPT { + auto clear_chain = [](auto **curr, auto... member) { + for(; *curr; *curr = std::exchange((*curr)->next, nullptr)) { + if constexpr(sizeof...(member) != 0u) { + static_assert(sizeof...(member) == 1u, "Assert in defense of the future me"); + for(auto **sub = (&((*curr)->*member), ...); *sub; *sub = std::exchange((*sub)->next, nullptr)) {} + } + } + }; + + for(auto **it = internal::meta_context::global(); *it; it = &(*it)->next) { + if(auto *node = *it; node->id == id) { + clear_chain(&node->prop); + clear_chain(&node->base); + clear_chain(&node->conv); + clear_chain(&node->ctor); + clear_chain(&node->data, &internal::meta_data_node::prop); + clear_chain(&node->func, &internal::meta_func_node::prop); + + node->id = {}; + node->dtor = nullptr; + *it = std::exchange(node->next, nullptr); + + break; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template +void meta_reset() ENTT_NOEXCEPT { + meta_reset(internal::meta_node::resolve()->id); +} + +/** + * @brief Resets all searchable types. + * + * @sa meta_reset + */ +inline void meta_reset() ENTT_NOEXCEPT { + while(*internal::meta_context::global()) { + meta_reset((*internal::meta_context::global())->id); + } +} + +} // 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 "adl_pointer.hpp" + +// #include "ctx.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 Default constructor. */ + meta_sequence_container() ENTT_NOEXCEPT = default; + + /** + * @brief Construct a proxy object for sequence containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + meta_sequence_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : value_type_node{internal::meta_node>>::resolve()}, + size_fn{&meta_sequence_container_traits::size}, + resize_fn{&meta_sequence_container_traits::resize}, + iter_fn{&meta_sequence_container_traits::iter}, + insert_fn{&meta_sequence_container_traits::insert}, + erase_fn{&meta_sequence_container_traits::erase}, + storage{std::move(instance)} {} + + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_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 ENTT_NOEXCEPT; + +private: + internal::meta_type_node *value_type_node = nullptr; + size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool (*resize_fn)(any &, size_type) = nullptr; + iterator (*iter_fn)(any &, const bool) = nullptr; + iterator (*insert_fn)(any &, const std::ptrdiff_t, meta_any &) = nullptr; + iterator (*erase_fn)(any &, const std::ptrdiff_t) = nullptr; + 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 Default constructor. */ + meta_associative_container() ENTT_NOEXCEPT = default; + + /** + * @brief Construct a proxy object for associative containers. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + meta_associative_container(std::in_place_type_t, any instance) ENTT_NOEXCEPT + : key_only_container{meta_associative_container_traits::key_only}, + key_type_node{internal::meta_node>>::resolve()}, + mapped_type_node{nullptr}, + value_type_node{internal::meta_node>>::resolve()}, + size_fn{&meta_associative_container_traits::size}, + clear_fn{&meta_associative_container_traits::clear}, + iter_fn{&meta_associative_container_traits::iter}, + insert_fn{&meta_associative_container_traits::insert}, + erase_fn{&meta_associative_container_traits::erase}, + find_fn{&meta_associative_container_traits::find}, + storage{std::move(instance)} { + if constexpr(!meta_associative_container_traits::key_only) { + mapped_type_node = internal::meta_node>>::resolve(); + } + } + + [[nodiscard]] inline bool key_only() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type key_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type mapped_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline meta_type value_type() const ENTT_NOEXCEPT; + [[nodiscard]] inline size_type size() const ENTT_NOEXCEPT; + inline bool clear(); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline bool erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const ENTT_NOEXCEPT; + +private: + bool key_only_container{}; + internal::meta_type_node *key_type_node = nullptr; + internal::meta_type_node *mapped_type_node = nullptr; + internal::meta_type_node *value_type_node = nullptr; + size_type (*size_fn)(const any &) ENTT_NOEXCEPT = nullptr; + bool (*clear_fn)(any &) = nullptr; + iterator (*iter_fn)(any &, const bool) = nullptr; + bool (*insert_fn)(any &, meta_any &, meta_any &) = nullptr; + bool (*erase_fn)(any &, meta_any &) = nullptr; + iterator (*find_fn)(any &, meta_any &) = nullptr; + any storage{}; +}; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + enum class operation : 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) = 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) = {std::in_place_type, std::move(const_cast(value))}; + } + break; + case operation::assoc: + if constexpr(is_complete_v>) { + *static_cast(other) = {std::in_place_type, std::move(const_cast(value))}; + } + break; + } + } + } + + void release() { + if(node && node->dtor && storage.owner()) { + node->dtor(storage.data()); + } + } + + meta_any(const meta_any &other, any ref) ENTT_NOEXCEPT + : storage{std::move(ref)}, + node{storage ? other.node : nullptr}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! @brief Default constructor. */ + meta_any() ENTT_NOEXCEPT + : storage{}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + node{internal::meta_node>>::resolve()}, + 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{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) ENTT_NOEXCEPT + : storage{std::move(other.storage)}, + node{std::exchange(other.node, nullptr)}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + 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(); + vtable = other.vtable; + storage = other.storage; + node = other.node; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) ENTT_NOEXCEPT { + release(); + vtable = std::exchange(other.vtable, &basic_vtable); + storage = std::move(other.storage); + node = std::exchange(other.node, nullptr); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + 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 ENTT_NOEXCEPT; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * + * @sa meta_func::invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * + * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + [[nodiscard]] const Type *try_cast() const { + if(const auto &info = type_id(); node && *node->info == info) { + return any_cast(&storage); + } else if(node) { + for(auto *it = node->base; it; it = it->next) { + const auto as_const = it->cast(as_ref()); + + if(const Type *base = as_const.template try_cast(); base) { + return base; + } + } + } + + return nullptr; + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if(const auto &info = type_id(); node && *node->info == info) { + return any_cast(&storage); + } else if(node) { + for(auto *it = node->base; it; it = it->next) { + if(Type *base = it->cast(as_ref()).template try_cast(); base) { + return base; + } + } + } + + return nullptr; + } + + /** + * @brief Tries to cast an instance to a given type. + * + * The type of the instance must be such that the cast is possible. + * + * @warning + * Attempting to perform an invalid cast results 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.storage.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 { + const auto other = allow_cast(internal::meta_node>>::resolve()); + + if constexpr(std::is_reference_v && !std::is_const_v>) { + return other.storage.owner() ? other : meta_any{}; + } else { + return 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() { + if(auto other = std::as_const(*this).allow_cast(internal::meta_node>>::resolve()); other) { + if(other.storage.owner()) { + std::swap(*this, other); + return true; + } + + return (static_cast> &>(storage).data() != nullptr); + } + + return false; + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + vtable = &basic_vtable>>; + storage.emplace(std::forward(args)...); + node = internal::meta_node>>::resolve(); + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + vtable = &basic_vtable; + storage.reset(); + node = nullptr; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_sequence_container proxy; + vtable(operation::seq, detached, &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_sequence_container proxy; + 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() ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_associative_container proxy; + vtable(operation::assoc, detached, &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const ENTT_NOEXCEPT { + any detached = storage.as_ref(); + meta_associative_container proxy; + 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 ENTT_NOEXCEPT { + meta_any ret{}; + vtable(operation::deref, storage, &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const { + return (!node && !other.node) || (node && other.node && *node->info == *other.node->info && storage == other.storage); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() ENTT_NOEXCEPT { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const ENTT_NOEXCEPT { + return meta_any{*this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return storage.owner(); + } + +private: + any storage; + internal::meta_type_node *node; + vtable_type *vtable; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_any &lhs, const meta_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template +meta_any make_meta(Args &&...args) { + return meta_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +meta_any forward_as_meta(Type &&value) { + return meta_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance.
+ * Handles are used to generate references to actual objects when needed. + */ +struct meta_handle { + /*! @brief Default constructor. */ + meta_handle() = default; + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) ENTT_NOEXCEPT + : meta_handle{} { + if constexpr(std::is_same_v, meta_any>) { + any = value.as_ref(); + } else { + any.emplace(value); + } + } + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(any); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Node type. */ + using node_type = internal::meta_prop_node; + + /** + * @brief Constructs an instance from a given node. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /** + * @brief Returns the stored key as a const reference. + * @return A wrapper containing the key stored with the property. + */ + [[nodiscard]] meta_any key() const { + return node->id.as_ref(); + } + + /** + * @brief Returns the stored value by copy. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Node type. */ + using node_type = internal::meta_data_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_data(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const ENTT_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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const ENTT_NOEXCEPT; + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.
+ * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(std::move(instance)); + } + + /** + * @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 ENTT_NOEXCEPT; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for registered 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(meta_any key) const { + for(auto curr: prop()) { + if(curr.key() == key) { + return curr; + } + } + + return nullptr; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Node type. */ + using node_type = internal::meta_func_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_func(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /*! @copydoc meta_type::id */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const ENTT_NOEXCEPT { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const ENTT_NOEXCEPT; + + /** + * @brief Invokes the underlying function, if possible. + * + * To invoke a member function, the parameters must be such that a cast or + * conversion to the required types is possible. Otherwise, an empty and + * thus invalid wrapper is returned.
+ * It must be possible to cast the instance to the parent type of the member + * function. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(std::move(instance), args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for registered 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(meta_any key) const { + for(auto curr: prop()) { + if(curr.key() == key) { + return curr; + } + } + + return nullptr; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + +private: + const node_type *node; +}; + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] std::decay_t().*Member)> lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, Pred pred) const { + std::decay_t*Member)> candidate{}; + size_type extent{sz + 1u}; + bool ambiguous{}; + + for(auto *curr = (node->*Member); curr; curr = curr->next) { + if(pred(curr) && curr->arity == sz) { + size_type direct{}; + size_type ext{}; + + for(size_type next{}; next < sz && next == (direct + ext) && args[next]; ++next) { + const auto type = args[next].type(); + const auto other = curr->arg(next); + + if(const auto &info = other.info(); info == type.info()) { + ++direct; + } else { + ext += internal::find_by<&node_type::base>(info, type.node) + || internal::find_by<&node_type::conv>(info, type.node) + || (type.node->conversion_helper && other.node->conversion_helper); + } + } + + if((direct + ext) == sz) { + if(ext < extent) { + candidate = curr; + extent = ext; + ambiguous = false; + } else if(ext == extent) { + ambiguous = true; + } + } + } + } + + return (candidate && !ambiguous) ? candidate : decltype(candidate){}; + } + +public: + /*! @brief Node type. */ + using node_type = internal::meta_type_node; + /*! @brief Node type. */ + using base_node_type = internal::meta_base_node; + /*! @brief Unsigned integer type. */ + using size_type = typename node_type::size_type; + + /*! @copydoc meta_prop::meta_prop */ + meta_type(const node_type *curr = nullptr) ENTT_NOEXCEPT + : node{curr} {} + + /** + * @brief Constructs an instance from a given base node. + * @param curr The base node with which to construct the instance. + */ + meta_type(const base_node_type *curr) ENTT_NOEXCEPT + : node{curr ? curr->type : nullptr} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const ENTT_NOEXCEPT { + return *node->info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const ENTT_NOEXCEPT { + return node->id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const ENTT_NOEXCEPT { + return node->size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(node->traits & internal::meta_traits::is_pointer); + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is a pointer-like one, false + * otherwise. + */ + [[nodiscard]] bool is_pointer_like() const ENTT_NOEXCEPT { + return !!(node->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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return !!(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 ENTT_NOEXCEPT { + return (node->templ != nullptr); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const ENTT_NOEXCEPT { + return node->templ ? node->templ->arity : size_type{}; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * + * @sa meta_class_template_tag + * + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const ENTT_NOEXCEPT { + return node->templ ? node->templ->type : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const ENTT_NOEXCEPT { + return index < template_arity() ? node->templ->arg(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 ENTT_NOEXCEPT { + return node->base; + } + + /** + * @brief Lookup function for registered base meta types. + * @param id Unique identifier. + * @return The registered base meta type for the given identifier, if any. + */ + [[nodiscard]] meta_type base(const id_type id) const { + return internal::find_by<&node_type::base>(id, node); + } + + /** + * @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 ENTT_NOEXCEPT { + return node->data; + } + + /** + * @brief Lookup function for registered meta data. + * + * Registered meta data of base classes will also be 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 { + return internal::find_by<&node_type::data>(id, node); + } + + /** + * @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 ENTT_NOEXCEPT { + return node->func; + } + + /** + * @brief Lookup function for registered meta functions. + * + * Registered meta functions of base classes will also be visited.
+ * In case of overloaded functions, the first one with the required + * identifier will be 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 { + return internal::find_by<&node_type::func>(id, node); + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * Parameters are such that a cast or conversion to the required types is + * possible. Otherwise, an empty and thus invalid wrapper is returned.
+ * If suitable, the implicitly generated default constructor is used. + * + * @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 { + const auto *candidate = lookup<&node_type::ctor>(args, sz, [](const auto *) { return true; }); + return candidate ? candidate->invoke(args) : ((!sz && node->default_constructor) ? node->default_constructor() : meta_any{}); + } + + /** + * @copybrief construct + * + * @sa construct + * + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * It must be possible to cast the instance to the parent type of the member + * function. + * + * @sa meta_func::invoke + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + const auto *candidate = lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + + for(auto it = base().begin(), last = base().end(); it != last && !candidate; ++it) { + candidate = it->lookup<&node_type::func>(args, sz, [id](const auto *curr) { return curr->id == id; }); + } + + return candidate ? candidate->invoke(std::move(instance), args) : meta_any{}; + } + + /** + * @copybrief invoke + * + * @sa invoke + * + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the new instance, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + 1u]{std::forward(args)...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member.
+ * The type of the value is such that a cast or conversion to the type of + * the variable is possible. Otherwise, invoking the setter does nothing. + * + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * + * It must be possible to cast the instance to the parent type of the data + * member. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const ENTT_NOEXCEPT { + return node->prop; + } + + /** + * @brief Lookup function for meta properties. + * + * Properties of base classes 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(meta_any key) const { + return internal::find_by<&internal::meta_type_node::prop>(key, node); + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return !(node == nullptr); + } + + /** + * @brief Checks if two objects refer to the same type. + * @param other The object with which to compare. + * @return True if the objects refer to the same type, false otherwise. + */ + [[nodiscard]] bool operator==(const meta_type &other) const ENTT_NOEXCEPT { + return (!node && !other.node) || (node && other.node && *node->info == *other.node->info); + } + +private: + const node_type *node; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const ENTT_NOEXCEPT { + return node; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + if(const auto &info = type.info(); node && *node->info == info) { + return as_ref(); + } else if(node) { + for(auto *it = node->conv; it; it = it->next) { + if(*it->type->info == info) { + return it->conv(*this); + } + } + + 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; + } + + for(auto *it = node->base; it; it = it->next) { + const auto as_const = it->cast(as_ref()); + + if(auto other = as_const.allow_cast(type); other) { + return other; + } + } + } + + return {}; +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast(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 ENTT_NOEXCEPT { + return node->type; +} + +[[nodiscard]] inline meta_type meta_func::ret() const ENTT_NOEXCEPT { + return node->ret; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const ENTT_NOEXCEPT { + return index < arity() ? node->arg(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; + + using deref_fn_type = meta_any(const any &, const std::ptrdiff_t); + + template + static meta_any deref_fn(const any &value, const std::ptrdiff_t pos) { + return meta_any{std::in_place_type::reference>, any_cast(value)[pos]}; + } + +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; + + meta_iterator() ENTT_NOEXCEPT + : deref{}, + offset{}, + handle{} {} + + template + explicit meta_iterator(It iter, const difference_type init) ENTT_NOEXCEPT + : deref{&deref_fn}, + offset{init}, + handle{std::move(iter)} {} + + meta_iterator &operator++() ENTT_NOEXCEPT { + return ++offset, *this; + } + + meta_iterator operator++(int value) ENTT_NOEXCEPT { + meta_iterator orig = *this; + offset += ++value; + return orig; + } + + meta_iterator &operator--() ENTT_NOEXCEPT { + return --offset, *this; + } + + meta_iterator operator--(int value) ENTT_NOEXCEPT { + meta_iterator orig = *this; + offset -= ++value; + return orig; + } + + [[nodiscard]] reference operator*() const { + return deref(handle, offset); + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return offset == other.offset; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + deref_fn_type *deref; + difference_type offset; + 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; + + meta_iterator() ENTT_NOEXCEPT + : vtable{}, + handle{} {} + + template + meta_iterator(std::integral_constant, It iter) ENTT_NOEXCEPT + : vtable{&basic_vtable}, + handle{std::move(iter)} {} + + meta_iterator &operator++() ENTT_NOEXCEPT { + vtable(operation::incr, handle, nullptr); + return *this; + } + + meta_iterator operator++(int) ENTT_NOEXCEPT { + meta_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const { + reference other; + vtable(operation::deref, handle, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const ENTT_NOEXCEPT { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + 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 ENTT_NOEXCEPT { + return value_type_node; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const ENTT_NOEXCEPT { + return size_fn(storage); +} + +/** + * @brief Resizes a container to contain a given number of elements. + * @param sz The new size of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::resize(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(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(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_fn(storage, it.offset, 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 erase_fn(storage, it.offset); +} + +/** + * @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 ENTT_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 ENTT_NOEXCEPT { + return key_only_container; +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const ENTT_NOEXCEPT { + return key_type_node; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const ENTT_NOEXCEPT { + return mapped_type_node; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const ENTT_NOEXCEPT { + return value_type_node; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const ENTT_NOEXCEPT { + return size_fn(storage); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return clear_fn(storage); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return iter_fn(storage, false); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return iter_fn(storage, true); +} + +/** + * @brief Inserts an element (a key/value pair) into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return insert_fn(storage, key, value); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline bool meta_associative_container::erase(meta_any key) { + return erase_fn(storage, key); +} + +/** + * @brief Returns an iterator to the element with a given key, if any. + * @param key The key of the element to search. + * @return An iterator to the element with the given key, if any. + */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::find(meta_any key) { + return find_fn(storage, key); +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const ENTT_NOEXCEPT { + return static_cast(storage); +} + +} // namespace entt + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_array = 0x0008, + is_enum = 0x0010, + is_class = 0x0020, + is_pointer = 0x0040, + is_meta_pointer_like = 0x0080, + is_meta_sequence_container = 0x0100, + is_meta_associative_container = 0x0200, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_prop_node *next; + const meta_any &id; + meta_any &value; +}; + +struct meta_base_node { + meta_base_node *next; + meta_type_node *const type; + meta_any (*const cast)(meta_any) ENTT_NOEXCEPT; +}; + +struct meta_conv_node { + meta_conv_node *next; + meta_type_node *const type; + meta_any (*const conv)(const meta_any &); +}; + +struct meta_ctor_node { + using size_type = std::size_t; + meta_ctor_node *next; + const size_type arity; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + meta_any (*const invoke)(meta_any *const); +}; + +struct meta_data_node { + using size_type = std::size_t; + id_type id; + const meta_traits traits; + meta_data_node *next; + meta_prop_node *prop; + const size_type arity; + meta_type_node *const type; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + bool (*const set)(meta_handle, meta_any); + meta_any (*const get)(meta_handle); +}; + +struct meta_func_node { + using size_type = std::size_t; + id_type id; + const meta_traits traits; + meta_func_node *next; + meta_prop_node *prop; + const size_type arity; + meta_type_node *const ret; + meta_type (*const arg)(const size_type) ENTT_NOEXCEPT; + meta_any (*const invoke)(meta_handle, meta_any *const); +}; + +struct meta_template_node { + using size_type = std::size_t; + const size_type arity; + meta_type_node *const type; + meta_type_node *(*const arg)(const size_type)ENTT_NOEXCEPT; +}; + +struct meta_type_node { + using size_type = std::size_t; + const type_info *info; + id_type id; + const meta_traits traits; + meta_type_node *next; + meta_prop_node *prop; + const size_type size_of; + meta_any (*const default_constructor)(); + double (*const conversion_helper)(void *, const void *); + const meta_template_node *const templ; + meta_ctor_node *ctor{nullptr}; + meta_base_node *base{nullptr}; + meta_conv_node *conv{nullptr}; + meta_data_node *data{nullptr}; + meta_func_node *func{nullptr}; + void (*dtor)(void *){nullptr}; +}; + +template +meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT; + +template +class ENTT_API meta_node { + static_assert(std::is_same_v>>, "Invalid type"); + + [[nodiscard]] static auto *meta_default_constructor() ENTT_NOEXCEPT { + if constexpr(std::is_default_constructible_v) { + return +[]() { return meta_any{std::in_place_type}; }; + } else { + return static_cast>(nullptr); + } + } + + [[nodiscard]] static auto *meta_conversion_helper() ENTT_NOEXCEPT { + if constexpr(std::is_arithmetic_v) { + return +[](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) { + return +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } else { + return static_cast>(nullptr); + } + } + + [[nodiscard]] static meta_template_node *meta_template_info() ENTT_NOEXCEPT { + if constexpr(is_complete_v>) { + static meta_template_node node{ + meta_template_traits::args_type::size, + meta_node::class_type>::resolve(), + [](const std::size_t index) ENTT_NOEXCEPT { return meta_arg_node(typename meta_template_traits::args_type{}, index); } + // tricks clang-format + }; + + return &node; + } else { + return nullptr; + } + } + +public: + [[nodiscard]] static meta_type_node *resolve() ENTT_NOEXCEPT { + static meta_type_node node{ + &type_id(), + {}, + internal::meta_traits::is_none + | (std::is_arithmetic_v ? internal::meta_traits::is_arithmetic : internal::meta_traits::is_none) + | (std::is_array_v ? internal::meta_traits::is_array : internal::meta_traits::is_none) + | (std::is_enum_v ? internal::meta_traits::is_enum : internal::meta_traits::is_none) + | (std::is_class_v ? internal::meta_traits::is_class : internal::meta_traits::is_none) + | (std::is_pointer_v ? internal::meta_traits::is_pointer : internal::meta_traits::is_none) + | (is_meta_pointer_like_v ? internal::meta_traits::is_meta_pointer_like : internal::meta_traits::is_none) + | (is_complete_v> ? internal::meta_traits::is_meta_sequence_container : internal::meta_traits::is_none) + | (is_complete_v> ? internal::meta_traits::is_meta_associative_container : internal::meta_traits::is_none), + nullptr, + nullptr, + size_of_v, + meta_default_constructor(), + meta_conversion_helper(), + meta_template_info() + // tricks clang-format + }; + + return &node; + } +}; + +template +[[nodiscard]] meta_type_node *meta_arg_node(type_list, const std::size_t index) ENTT_NOEXCEPT { + meta_type_node *args[sizeof...(Args) + 1u]{nullptr, internal::meta_node>>::resolve()...}; + return args[index + 1u]; +} + +template +[[nodiscard]] static std::decay_t().*Member)> find_by(const Type &info_or_id, const internal::meta_type_node *node) ENTT_NOEXCEPT { + for(auto *curr = node->*Member; curr; curr = curr->next) { + if constexpr(std::is_same_v) { + if(*curr->type->info == info_or_id) { + return curr; + } + } else if constexpr(std::is_same_v) { + if(curr->type->id == info_or_id) { + return curr; + } + } else { + if(curr->id == info_or_id) { + return curr; + } + } + } + + for(auto *curr = node->base; curr; curr = curr->next) { + if(auto *ret = find_by(info_or_id, curr->type); ret) { + return ret; + } + } + + return nullptr; +} + +} // 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 { + /** + * @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 { + /** + * @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 { + /** + * @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 { + /** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + template + static constexpr bool value = true; + /** + * Internal details not to be documented. + * @endcond + */ +}; + +} // namespace entt + +#endif + +// #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +// #include "../core/iterator.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 = Type; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using node_type = Node; + + meta_range_iterator() ENTT_NOEXCEPT + : it{} {} + + meta_range_iterator(node_type *head) ENTT_NOEXCEPT + : it{head} {} + + meta_range_iterator &operator++() ENTT_NOEXCEPT { + return (it = it->next), *this; + } + + meta_range_iterator operator++(int) ENTT_NOEXCEPT { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return it; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] bool operator==(const meta_range_iterator &other) const ENTT_NOEXCEPT { + return it == other.it; + } + + [[nodiscard]] bool operator!=(const meta_range_iterator &other) const ENTT_NOEXCEPT { + return !(*this == other); + } + +private: + node_type *it; +}; + +} // 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 Node Type of meta nodes iterated. + */ +template +struct meta_range final { + /*! @brief Node type. */ + using node_type = Node; + /*! @brief Input iterator type. */ + using iterator = internal::meta_range_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = iterator; + + /*! @brief Default constructor. */ + meta_range() ENTT_NOEXCEPT = default; + + /** + * @brief Constructs a meta range from a given node. + * @param head The underlying node with which to construct the range. + */ + meta_range(node_type *head) ENTT_NOEXCEPT + : node{head} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first meta object of the range. + */ + [[nodiscard]] const_iterator cbegin() const ENTT_NOEXCEPT { + return iterator{node}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last meta object of the + * range. + */ + [[nodiscard]] const_iterator cend() const ENTT_NOEXCEPT { + return iterator{}; + } + + /*! @copydoc cend */ + [[nodiscard]] iterator end() const ENTT_NOEXCEPT { + return cend(); + } + +private: + node_type *node{nullptr}; +}; + +} // namespace entt + +#endif + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "ctx.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() ENTT_NOEXCEPT { + return internal::meta_node>>::resolve(); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() ENTT_NOEXCEPT { + return *internal::meta_context::global(); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) ENTT_NOEXCEPT { + for(auto &&curr: resolve()) { + if(curr.id() == id) { + return curr; + } + } + + return {}; +} + +/** + * @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) ENTT_NOEXCEPT { + for(auto &&curr: resolve()) { + if(curr.info() == info) { + return curr; + } + } + + return {}; +} + +} // 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 "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = true; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @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 { + /*! @brief Meta data return type. */ + using return_type = Ret &; + /*! @brief Meta data arguments. */ + using args_type = std::conditional_t, type_list<>, type_list>; + + /*! @brief True if the meta data is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta data is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v; +}; + +/** + * @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 { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = std::conditional_t>, Type>, type_list, type_list>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = std::is_base_of_v>, Type> && std::is_const_v>; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = !std::is_base_of_v>, Type>; +}; + +/** + * @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 { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = type_list<>; + + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr auto is_const = false; + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr auto is_static = true; +}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret 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. + * @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 +meta_any meta_dispatch([[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{std::in_place_type, std::forward(value)}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{std::in_place_type &>, std::as_const(value)}; + } else { + static_assert(std::is_same_v, "Policy not supported"); + return meta_any{std::forward(value)}; + } +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) ENTT_NOEXCEPT { + return internal::meta_arg_node(Type{}, 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. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] meta_any meta_getter([[maybe_unused]] meta_handle instance) { + if constexpr(std::is_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(std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(std::invoke(Data, *fallback)); + } + } + } + + return meta_any{}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{}; + } else { + return meta_dispatch(*Data); + } + } else { + return meta_dispatch(Data); + } +} + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(Candidate &&candidate, Args &&...args) { + if constexpr(std::is_same_v, void>) { + std::invoke(candidate, args...); + return meta_any{std::in_place_type}; + } else { + return meta_dispatch(std::invoke(candidate, args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke([[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(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(std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{}; +} + +template +[[nodiscard]] meta_any meta_construct(meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/** + * @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]] meta_any meta_invoke([[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *const args) { + return internal::meta_invoke(std::move(instance), std::forward(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]] meta_any meta_invoke(meta_handle instance, meta_any *const args) { + return internal::meta_invoke(std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @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 internal::meta_construct(args, std::index_sequence_for{}); +} + +/** + * @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 args Parameters to use to _invoke_ the object. + * @param candidate The actual object to _invoke_. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static) { + return internal::meta_invoke({}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(*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 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]] meta_any meta_construct(meta_any *const args) { + return meta_construct(Candidate, 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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" +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +#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 "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + enum class operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get + }; + + enum class policy : std::uint8_t { + owner, + ref, + cref + }; + + using storage_type = std::aligned_storage_t; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + template + static constexpr bool in_situ = Len && alignof(Type) <= alignof(storage_type) && sizeof(Type) <= sizeof(storage_type) && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable([[maybe_unused]] const operation op, [[maybe_unused]] const basic_any &value, [[maybe_unused]] 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 *static_cast(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) { + if constexpr(!std::is_void_v) { + info = &type_id>>(); + 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) Type{std::forward(args)...}; + } else { + new(&storage) Type(std::forward(args)...); + } + } else { + if constexpr(sizeof...(Args) != 0u && std::is_aggregate_v) { + instance = new Type{std::forward(args)...}; + } else { + instance = new Type(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const policy pol) ENTT_NOEXCEPT + : instance{other.data()}, + 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() ENTT_NOEXCEPT + : instance{}, + info{&type_id()}, + vtable{}, + mode{policy::owner} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : basic_any{} { + 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{} { + initialize>(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) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_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() ENTT_NOEXCEPT { + return (!vtable || mode == policy::cref) ? nullptr : const_cast(vtable(operation::get, *this, nullptr)); + } + + /** + * @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) ENTT_NOEXCEPT { + return *info == req ? data() : nullptr; + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + 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 any &other) { + if(vtable && mode != policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(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); + } + + 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 ENTT_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. + */ + bool operator==(const basic_any &other) const ENTT_NOEXCEPT { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() ENTT_NOEXCEPT { + return basic_any{*this, (mode == policy::cref ? policy::cref : policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const ENTT_NOEXCEPT { + return basic_any{*this, policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[nodiscard]] bool owner() const ENTT_NOEXCEPT { + return (mode == policy::owner); + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + policy mode; +}; + +/** + * @brief Checks if two wrappers differ in their content. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param lhs A wrapper, either empty or not. + * @param rhs A wrapper, either empty or not. + * @return True if the two wrappers differ in their content, false otherwise. + */ +template +[[nodiscard]] inline bool operator!=(const basic_any &lhs, const basic_any &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +Type any_cast(const basic_any &data) ENTT_NOEXCEPT { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &data) ENTT_NOEXCEPT { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +Type any_cast(basic_any &&data) ENTT_NOEXCEPT { + 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) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +Type *any_cast(basic_any *data) ENTT_NOEXCEPT { + const auto &info = type_id>>(); + // last attempt to make wrappers for const references return their values + return static_cast(static_cast, Type> *>(data)->data(info)); +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +basic_any make_any(Args &&...args) { + return basic_any{std::in_place_type, std::forward(args)...}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template::length, std::size_t Align = basic_any::alignment, typename Type> +basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::decay_t, Type>>, std::forward(value)}; +} + +} // 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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include +#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) ENTT_NOEXCEPT + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) ENTT_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...)) ENTT_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) ENTT_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() ENTT_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() ENTT_NOEXCEPT + : storage{}, + vtable{} {} + + /** + * @brief Constructs a poly by directly initializing the new object. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_poly(std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + vtable{poly_vtable::template instance>>()} {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template>, basic_poly>>> + basic_poly(Type &&value) ENTT_NOEXCEPT + : basic_poly{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const ENTT_NOEXCEPT { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const ENTT_NOEXCEPT { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void *data() ENTT_NOEXCEPT { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + 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 ENTT_NOEXCEPT { + return static_cast(storage); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type *operator->() ENTT_NOEXCEPT { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type *operator->() const ENTT_NOEXCEPT { + return this; + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() ENTT_NOEXCEPT { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const ENTT_NOEXCEPT { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage; + vtable_type vtable; +}; + +} // namespace entt + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 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 ENTT_NOEXCEPT {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() ENTT_NOEXCEPT { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() ENTT_NOEXCEPT { + if(alive()) { + current = state::failed; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() ENTT_NOEXCEPT { + if(current == state::running) { + current = state::paused; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() ENTT_NOEXCEPT { + if(current == state::paused) { + current = state::running; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() ENTT_NOEXCEPT { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + if(alive()) { + current = state::aborted; + + if(immediately) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const ENTT_NOEXCEPT { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const ENTT_NOEXCEPT { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const ENTT_NOEXCEPT { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { + return current == state::rejected; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::uninitialized: + next(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::integral_constant{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + next(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&...args) + : Func{std::forward(args)...} {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()( + delta, + data, + [this]() { this->succeed(); }, + [this]() { this->fail(); }); + } +}; + +} // namespace entt + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.
+ * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template +class process { + enum class state : 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 ENTT_NOEXCEPT {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() ENTT_NOEXCEPT { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() ENTT_NOEXCEPT { + if(alive()) { + current = state::failed; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() ENTT_NOEXCEPT { + if(current == state::running) { + current = state::paused; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() ENTT_NOEXCEPT { + if(current == state::paused) { + current = state::running; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() ENTT_NOEXCEPT { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediately Requests an immediate operation. + */ + void abort(const bool immediately = false) { + if(alive()) { + current = state::aborted; + + if(immediately) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const ENTT_NOEXCEPT { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const ENTT_NOEXCEPT { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const ENTT_NOEXCEPT { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const ENTT_NOEXCEPT { + return current == state::rejected; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::uninitialized: + next(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::integral_constant{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + next(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&...args) + : Func{std::forward(args)...} {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()( + delta, + data, + [this]() { this->succeed(); }, + [this]() { this->fail(); }); + } +}; + +} // 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) ENTT_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() = default; + + /*! @brief Default move constructor. */ + scheduler(scheduler &&) = default; + + /*! @brief Default move assignment operator. @return This scheduler. */ + scheduler &operator=(scheduler &&) = default; + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return handlers.size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return handlers.empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value is an opaque object that can be used to attach a child to + * the given process. The child is automatically scheduled when the process + * terminates and only if the process returns with success. + * + * Example of use (pseudocode): + * + * @code{.cpp} + * // schedules a task in the form of a process class + * scheduler.attach(arguments...) + * // appends a child in the form of a lambda function + * .then([](auto delta, void *, auto succeed, auto fail) { + * // code + * }) + * // appends a child in the form of another process class + * .then(); + * @endcode + * + * @tparam Proc Type of process to schedule. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return An opaque object to use to concatenate processes. + */ + template + auto attach(Args &&...args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + auto proc = typename process_handler::instance_type{new Proc{std::forward(args)...}, &scheduler::deleter}; + 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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; + + dense_map_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_map_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_map_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_map_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + dense_map_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_map_iterator operator--(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_map_iterator copy = *this; + return (copy += value); + } + + dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->element.first, it->element.second}; + } + + template + friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_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; + + dense_map_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].next, *this; + } + + dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const allocator_type &allocator) + : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_map{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 + * 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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 +// #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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 "../config/config.h" + + +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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "../config/config.h" + +// #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 Default constructor. */ + resource() ENTT_NOEXCEPT + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(std::shared_ptr res) ENTT_NOEXCEPT + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) ENTT_NOEXCEPT = default; + + /*! @brief Default move constructor. */ + resource(resource &&) ENTT_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, Type &res) ENTT_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) ENTT_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) ENTT_NOEXCEPT + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) ENTT_NOEXCEPT = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) ENTT_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) ENTT_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]] Type &operator*() const ENTT_NOEXCEPT { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator Type &() const ENTT_NOEXCEPT { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] Type *operator->() const ENTT_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 ENTT_NOEXCEPT { + return static_cast(value); + } + + /** + * @brief Returns the number of handles pointing the same resource. + * @return The number of handles pointing the same resource. + */ + [[nodiscard]] long use_count() const ENTT_NOEXCEPT { + return value.use_count(); + } + +private: + std::shared_ptr 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) ENTT_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) ENTT_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; + + resource_cache_iterator() ENTT_NOEXCEPT = default; + + resource_cache_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + resource_cache_iterator(const resource_cache_iterator, Other> &other) ENTT_NOEXCEPT + : it{other.it} {} + + resource_cache_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + resource_cache_iterator operator++(int) ENTT_NOEXCEPT { + resource_cache_iterator orig = *this; + return ++(*this), orig; + } + + resource_cache_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + resource_cache_iterator operator--(int) ENTT_NOEXCEPT { + resource_cache_iterator orig = *this; + return operator--(), orig; + } + + resource_cache_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + resource_cache_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + resource_cache_iterator copy = *this; + return (copy += value); + } + + resource_cache_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + resource_cache_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].first, resource{it[value].second}}; + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return (*this)[0]; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + template + friend std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return pool.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return pool.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_NOEXCEPT { + return pool.first().empty(); + } + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return pool.first().size(); + } + + /*! @brief Clears a cache. */ + void clear() ENTT_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 "../config/config.h" + +// #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 Default constructor. */ + resource() ENTT_NOEXCEPT + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(std::shared_ptr res) ENTT_NOEXCEPT + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) ENTT_NOEXCEPT = default; + + /*! @brief Default move constructor. */ + resource(resource &&) ENTT_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, Type &res) ENTT_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) ENTT_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) ENTT_NOEXCEPT + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) ENTT_NOEXCEPT = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) ENTT_NOEXCEPT = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) ENTT_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) ENTT_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]] Type &operator*() const ENTT_NOEXCEPT { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator Type &() const ENTT_NOEXCEPT { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] Type *operator->() const ENTT_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 ENTT_NOEXCEPT { + return static_cast(value); + } + + /** + * @brief Returns the number of handles pointing the same resource. + * @return The number of handles pointing the same resource. + */ + [[nodiscard]] long use_count() const ENTT_NOEXCEPT { + return value.use_count(); + } + +private: + std::shared_ptr 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) ENTT_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) ENTT_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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + 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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_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 ENTT_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 ENTT_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 ENTT_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) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../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 +// #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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "../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 10 +#define ENTT_VERSION_PATCH 0 + +#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_NOEXCEPT noexcept +# define ENTT_NOEXCEPT_IF(expr) noexcept(expr) +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_NOEXCEPT +# define ENTT_NOEXCEPT_IF(...) +# 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(...) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, ...) assert(condition) +#endif + +#ifdef ENTT_NO_ETO +# define ENTT_IGNORE_IF_EMPTY false +#else +# define ENTT_IGNORE_IF_EMPTY true +#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 "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 +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 "../config/config.h" + + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Pointer type. */ + using pointer = Type *; + + /*! @brief Default copy constructor, deleted on purpose. */ + input_iterator_pointer(const input_iterator_pointer &) = delete; + + /*! @brief Default move constructor. */ + input_iterator_pointer(input_iterator_pointer &&) = default; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + input_iterator_pointer(Type &&val) + : value{std::move(val)} {} + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This proxy object. + */ + input_iterator_pointer &operator=(const input_iterator_pointer &) = delete; + + /** + * @brief Default move assignment operator. + * @return This proxy object. + */ + input_iterator_pointer &operator=(input_iterator_pointer &&) = default; + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] pointer operator->() ENTT_NOEXCEPT { + return std::addressof(value); + } + +private: + Type value; +}; + +/** + * @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. */ + iterable_adaptor() = default; + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + iterable_adaptor(iterator from, sentinel to) + : first{from}, + last{to} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] iterator begin() const ENTT_NOEXCEPT { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] sentinel end() const ENTT_NOEXCEPT { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] iterator cbegin() const ENTT_NOEXCEPT { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] sentinel cend() const ENTT_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 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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(std::allocator_traits::propagate_on_container_swap::value || lhs == rhs, "Cannot swap the containers"); + + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } +} + +/** + * @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) ENTT_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) ENTT_NOEXCEPT { + ENTT_ASSERT(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) ENTT_NOEXCEPT { + ENTT_ASSERT(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @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. + */ + allocation_deleter(const allocator_type &alloc) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + void operator()(pointer ptr) { + 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 +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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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([&](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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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; + + dense_map_iterator() ENTT_NOEXCEPT + : it{} {} + + dense_map_iterator(const It iter) ENTT_NOEXCEPT + : it{iter} {} + + template && std::is_constructible_v>> + dense_map_iterator(const dense_map_iterator &other) ENTT_NOEXCEPT + : it{other.it} {} + + dense_map_iterator &operator++() ENTT_NOEXCEPT { + return ++it, *this; + } + + dense_map_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + dense_map_iterator &operator--() ENTT_NOEXCEPT { + return --it, *this; + } + + dense_map_iterator operator--(int) ENTT_NOEXCEPT { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + dense_map_iterator &operator+=(const difference_type value) ENTT_NOEXCEPT { + it += value; + return *this; + } + + dense_map_iterator operator+(const difference_type value) const ENTT_NOEXCEPT { + dense_map_iterator copy = *this; + return (copy += value); + } + + dense_map_iterator &operator-=(const difference_type value) ENTT_NOEXCEPT { + return (*this += -value); + } + + dense_map_iterator operator-(const difference_type value) const ENTT_NOEXCEPT { + return (*this + -value); + } + + [[nodiscard]] reference operator[](const difference_type value) const ENTT_NOEXCEPT { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it->element.first, it->element.second}; + } + + template + friend std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator==(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + + template + friend bool operator<(const dense_map_iterator &, const dense_map_iterator &) ENTT_NOEXCEPT; + +private: + It it; +}; + +template +[[nodiscard]] std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +template +[[nodiscard]] bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return rhs < lhs; +} + +template +[[nodiscard]] bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_NOEXCEPT { + return !(lhs > rhs); +} + +template +[[nodiscard]] bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) ENTT_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; + + dense_map_local_iterator() ENTT_NOEXCEPT + : it{}, + offset{} {} + + dense_map_local_iterator(It iter, const std::size_t pos) ENTT_NOEXCEPT + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + dense_map_local_iterator(const dense_map_local_iterator &other) ENTT_NOEXCEPT + : it{other.it}, + offset{other.offset} {} + + dense_map_local_iterator &operator++() ENTT_NOEXCEPT { + return offset = it[offset].next, *this; + } + + dense_map_local_iterator operator++(int) ENTT_NOEXCEPT { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const ENTT_NOEXCEPT { + return operator*(); + } + + [[nodiscard]] reference operator*() const ENTT_NOEXCEPT { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] std::size_t index() const ENTT_NOEXCEPT { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_NOEXCEPT { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) ENTT_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 ENTT_NOEXCEPT { + return fast_mod(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) { + packed.first()[pos] = std::move(packed.first().back()); + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + 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 bucket_count Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const allocator_type &allocator) + : dense_map{bucket_count, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param bucket_count Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type bucket_count, const hasher &hash, const allocator_type &allocator) + : dense_map{bucket_count, 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 bucket_count 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 bucket_count, 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(bucket_count); + } + + /*! @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 &&) = 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 &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_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 ENTT_NOEXCEPT { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const ENTT_NOEXCEPT { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() ENTT_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 ENTT_NOEXCEPT { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const ENTT_NOEXCEPT { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return packed.first().size(); + } + + /*! @brief Clears the container. */ + void clear() ENTT_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 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 + * 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 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 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([[maybe_unused]] 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 count New number of buckets. + */ + void rehash(const size_type count) { + auto value = (std::max)(count, minimum_capacity); + value = (std::max)(value, static_cast(size() / max_load_factor())); + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + std::fill(sparse.first().begin(), sparse.first().end(), (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 count New number of elements. + */ + void reserve(const size_type count) { + packed.first().reserve(count); + rehash(static_cast(std::ceil(count / 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 "../config/config.h" + +// #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 Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Type First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_unique; + +/** + * @brief Removes duplicates types from a type list. + * @tparam Type One of the types provided by the given type list. + * @tparam Other The other types provided by the given type list. + */ +template +struct type_list_unique> { + /*! @brief A type list without duplicate types. */ + using type = std::conditional_t< + (std::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 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 = std::add_const_t; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +} // 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>> + compressed_pair_element() + : value{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : value{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return value; + } + + [[nodiscard]] const_reference get() const ENTT_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>> + compressed_pair_element() + : base_type{} {} + + template>, compressed_pair_element>>> + compressed_pair_element(Args &&args) + : base_type{std::forward(args)} {} + + template + compressed_pair_element(std::tuple args, std::index_sequence) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] reference get() ENTT_NOEXCEPT { + return *this; + } + + [[nodiscard]] const_reference get() const ENTT_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() + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) = 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) + : 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) + : 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) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] first_type &first() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] const first_type &first() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] second_type &second() ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] const second_type &second() const ENTT_NOEXCEPT { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + void swap(compressed_pair &other) { + 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 + decltype(auto) get() ENTT_NOEXCEPT { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + decltype(auto) get() const ENTT_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 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 +// #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 "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @cond TURN_OFF_DOXYGEN + * Internal details not to be documented. + */ + +namespace internal { + +template +struct fnv1a_traits; + +template<> +struct fnv1a_traits { + using type = std::uint32_t; + static constexpr std::uint32_t offset = 2166136261; + static constexpr std::uint32_t prime = 16777619; +}; + +template<> +struct fnv1a_traits { + using type = std::uint64_t; + static constexpr std::uint64_t offset = 14695981039346656037ull; + static constexpr std::uint64_t prime = 1099511628211ull; +}; + +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) ENTT_NOEXCEPT: repr{str} {} + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) ENTT_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) ENTT_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) ENTT_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]) ENTT_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) ENTT_NOEXCEPT { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() ENTT_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) ENTT_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]) ENTT_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) ENTT_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 ENTT_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 ENTT_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 ENTT_NOEXCEPT { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const ENTT_NOEXCEPT { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) ENTT_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) ENTT_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() ENTT_NOEXCEPT { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() ENTT_NOEXCEPT { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr std::string_view type_name(int) ENTT_NOEXCEPT { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] static std::string_view type_name(char) ENTT_NOEXCEPT { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] static constexpr id_type type_hash(int) ENTT_NOEXCEPT { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] static id_type type_hash(char) ENTT_NOEXCEPT { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // 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() ENTT_NOEXCEPT { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() ENTT_NOEXCEPT { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const ENTT_NOEXCEPT { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() ENTT_NOEXCEPT { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const ENTT_NOEXCEPT { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +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) ENTT_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 ENTT_NOEXCEPT { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const ENTT_NOEXCEPT { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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) ENTT_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() ENTT_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 &&) ENTT_NOEXCEPT { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief 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 ENTT_NOEXCEPT { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) ENTT_NOEXCEPT { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) ENTT_NOEXCEPT { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + y_combinator(Func recursive) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + decltype(auto) operator()(Args &&...args) const { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + decltype(auto) operator()(Args &&...args) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // 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 "../config/config.h" + +// #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 +auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(internal::function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal + +/** + * Internal details not to be documented. + * @endcond + */ + +/*! @brief Used to wrap a function or a member of a specified type. */ +template +struct connect_arg_t {}; + +/*! @brief Constant of type connect_arg_t used to disambiguate calls. */ +template +inline constexpr connect_arg_t connect_arg{}; + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) ENTT_NOEXCEPT { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) ENTT_NOEXCEPT { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() ENTT_NOEXCEPT + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate and connects a free function or an unbound + * member. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + delegate(connect_arg_t) ENTT_NOEXCEPT { + connect(); + } + + /** + * @brief Constructs a delegate and connects a free function with payload or + * a bound member. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&value_or_instance) ENTT_NOEXCEPT { + connect(std::forward(value_or_instance)); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) ENTT_NOEXCEPT { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() ENTT_NOEXCEPT { + 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) ENTT_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) ENTT_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) ENTT_NOEXCEPT { + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() ENTT_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 ENTT_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 ENTT_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 ENTT_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) ENTT_NOEXCEPT { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // 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; + static_assert(std::is_same_v, "Invalid value type"); + 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() + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : 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) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) ENTT_NOEXCEPT + : 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) ENTT_NOEXCEPT + : 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) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + 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) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + return calls.get_allocator(); + } + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + 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() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) ENTT_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) ENTT_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 ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @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(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {std::move(conn), signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + 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() ENTT_NOEXCEPT = 0; + virtual std::size_t size() const ENTT_NOEXCEPT = 0; +}; + +template +class dispatcher_handler final: public basic_dispatcher_handler { + static_assert(std::is_same_v>, "Invalid event 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() ENTT_NOEXCEPT override { + events.clear(); + } + + [[nodiscard]] auto bucket() ENTT_NOEXCEPT { + using sink_type = typename sigh::sink_type; + return sink_type{signal}; + } + + void trigger(Event event) { + signal.publish(event); + } + + template + void enqueue(Args &&...args) { + if constexpr(std::is_aggregate_v) { + events.push_back(Event{std::forward(args)...}); + } else { + events.emplace_back(std::forward(args)...); + } + } + + std::size_t size() const ENTT_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 `Event`, listeners are such that they can be invoked with an argument of + * type `Event &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + * + * @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 pretty 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) { + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = pools.second(); + ptr = std::allocate_shared>(allocator, allocator); + } + + return static_cast &>(*ptr); + } + + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + auto &container = pools.first(); + + if(const auto it = container.find(id); it != container.end()) { + 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) ENTT_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) ENTT_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) ENTT_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 ENTT_NOEXCEPT { + return pools.second(); + } + + /** + * @brief Returns the number of pending events for a given type. + * @tparam Event 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 ENTT_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 ENTT_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(Event &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Event Type of event of which to get the sink. + * @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 Event Type of event to trigger. + * @param event An instance of the given type of event. + */ + template + void trigger(Event &&event = {}) { + trigger(type_hash>::value(), std::forward(event)); + } + + /** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Event Type of event to trigger. + * @param event 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, Event &&event = {}) { + assure>(id).trigger(std::forward(event)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void enqueue(Args &&...args) { + enqueue_hint(type_hash::value(), std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Event Type of event to enqueue. + * @param event An instance of the given type of event. + */ + template + void enqueue(Event &&event) { + enqueue_hint(type_hash>::value(), std::forward(event)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Event 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 Event Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param event An instance of the given type of event. + */ + template + void enqueue_hint(const id_type id, Event &&event) { + assure>(id).enqueue(std::forward(event)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + for(auto &&cpool: pools.first()) { + cpool.second->disconnect(value_or_instance); + } + } + + /** + * @brief Discards all the events stored so far in a given queue. + * @tparam Event 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() ENTT_NOEXCEPT { + for(auto &&cpool: pools.first()) { + cpool.second->clear(); + } + } + + /** + * @brief Delivers all the pending events of a given queue. + * @tparam Event 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 +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * The emitter class template follows the CRTP idiom. To create a custom emitter + * type, derived classes must inherit directly from the base class as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Pools for the type of events are created internally on the fly. It's not + * required to specify in advance the full list of accepted types.
+ * Moreover, whenever an event is published, an emitter provides the listeners + * with a reference to itself along with a reference to the event. Therefore + * listeners have an handy way to work with it without incurring in the need of + * capturing a reference to the emitter. + * + * @tparam Derived Actual type of emitter that extends the class template. + */ +template +class emitter { + struct basic_pool { + virtual ~basic_pool() = default; + virtual bool empty() const ENTT_NOEXCEPT = 0; + virtual void clear() ENTT_NOEXCEPT = 0; + }; + + template + struct pool_handler final: basic_pool { + static_assert(std::is_same_v>, "Invalid event type"); + + using listener_type = std::function; + using element_type = std::pair; + using container_type = std::list; + using connection_type = typename container_type::iterator; + + [[nodiscard]] bool empty() const ENTT_NOEXCEPT override { + auto pred = [](auto &&element) { return element.first; }; + + return std::all_of(once_list.cbegin(), once_list.cend(), pred) + && std::all_of(on_list.cbegin(), on_list.cend(), pred); + } + + void clear() ENTT_NOEXCEPT override { + if(publishing) { + for(auto &&element: once_list) { + element.first = true; + } + + for(auto &&element: on_list) { + element.first = true; + } + } else { + once_list.clear(); + on_list.clear(); + } + } + + connection_type once(listener_type listener) { + return once_list.emplace(once_list.cend(), false, std::move(listener)); + } + + connection_type on(listener_type listener) { + return on_list.emplace(on_list.cend(), false, std::move(listener)); + } + + void erase(connection_type conn) { + conn->first = true; + + if(!publishing) { + auto pred = [](auto &&element) { return element.first; }; + once_list.remove_if(pred); + on_list.remove_if(pred); + } + } + + void publish(Event &event, Derived &ref) { + container_type swap_list; + once_list.swap(swap_list); + + publishing = true; + + for(auto &&element: on_list) { + element.first ? void() : element.second(event, ref); + } + + for(auto &&element: swap_list) { + element.first ? void() : element.second(event, ref); + } + + publishing = false; + + on_list.remove_if([](auto &&element) { return element.first; }); + } + + private: + bool publishing{false}; + container_type once_list{}; + container_type on_list{}; + }; + + template + [[nodiscard]] pool_handler *assure() { + if(auto &&ptr = pools[type_hash::value()]; !ptr) { + auto *cpool = new pool_handler{}; + ptr.reset(cpool); + return cpool; + } else { + return static_cast *>(ptr.get()); + } + } + + template + [[nodiscard]] const pool_handler *assure() const { + const auto it = pools.find(type_hash::value()); + return (it == pools.cend()) ? nullptr : static_cast *>(it->second.get()); + } + +public: + /** @brief Type of listeners accepted for the given event. */ + template + using listener = typename pool_handler::listener_type; + + /** + * @brief Generic connection type for events. + * + * Type of the connection object returned by the event emitter whenever a + * listener for the given type is registered.
+ * It can be used to break connections still in use. + * + * @tparam Event Type of event for which the connection is created. + */ + template + struct connection: private pool_handler::connection_type { + /** @brief Event emitters are friend classes of connections. */ + friend class emitter; + + /*! @brief Default constructor. */ + connection() ENTT_NOEXCEPT = default; + + /** + * @brief Creates a connection that wraps its underlying instance. + * @param conn A connection object to wrap. + */ + connection(typename pool_handler::connection_type conn) + : pool_handler::connection_type{std::move(conn)} {} + }; + + /*! @brief Default constructor. */ + emitter() = default; + + /*! @brief Default destructor. */ + virtual ~emitter() ENTT_NOEXCEPT { + static_assert(std::is_base_of_v, Derived>, "Incorrect use of the class template"); + } + + /*! @brief Default move constructor. */ + emitter(emitter &&) = default; + + /*! @brief Default move assignment operator. @return This emitter. */ + emitter &operator=(emitter &&) = default; + + /** + * @brief Emits the given event. + * + * All the listeners registered for the specific event type are invoked with + * the given event. The event type must either have a proper constructor for + * the arguments provided or be an aggregate type. + * + * @tparam Event Type of event to publish. + * @tparam Args Types of arguments to use to construct the event. + * @param args Parameters to use to initialize the event. + */ + template + void publish(Args &&...args) { + Event instance{std::forward(args)...}; + assure()->publish(instance, *static_cast(this)); + } + + /** + * @brief Registers a long-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * more than once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection on(listener instance) { + return assure()->on(std::move(instance)); + } + + /** + * @brief Registers a short-lived listener with the event emitter. + * + * This method can be used to register a listener designed to be invoked + * only once for the given event type.
+ * The connection returned by the method can be freely discarded. It's meant + * to be used later to disconnect the listener if required. + * + * The listener is as a callable object that can be moved and the type of + * which is _compatible_ with `void(Event &, Derived &)`. + * + * @note + * Whenever an event is emitted, the emitter provides the listener with a + * reference to the derived class. Listeners don't have to capture those + * instances for later uses. + * + * @tparam Event Type of event to which to connect the listener. + * @param instance The listener to register. + * @return Connection object that can be used to disconnect the listener. + */ + template + connection once(listener instance) { + return assure()->once(std::move(instance)); + } + + /** + * @brief Disconnects a listener from the event emitter. + * + * Do not use twice the same connection to disconnect a listener, it results + * in undefined behavior. Once used, discard the connection object. + * + * @tparam Event Type of event of the connection. + * @param conn A valid connection. + */ + template + void erase(connection conn) { + assure()->erase(std::move(conn)); + } + + /** + * @brief Disconnects all the listeners for the given event type. + * + * All the connections previously returned for the given event are + * invalidated. Using them results in undefined behavior. + * + * @tparam Event Type of event to reset. + */ + template + void clear() { + assure()->clear(); + } + + /** + * @brief Disconnects all the listeners. + * + * All the connections previously returned are invalidated. Using them + * results in undefined behavior. + */ + void clear() ENTT_NOEXCEPT { + for(auto &&cpool: pools) { + cpool.second->clear(); + } + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Event Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + [[nodiscard]] bool empty() const { + const auto *cpool = assure(); + return !cpool || cpool->empty(); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return std::all_of(pools.cbegin(), pools.cend(), [](auto &&cpool) { + return cpool.second->empty(); + }); + } + +private: + dense_map, identity> pools{}; +}; + +} // namespace entt + +#endif + +// #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "delegate.hpp" + +// #include "fwd.hpp" + + +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; + static_assert(std::is_same_v, "Invalid value type"); + 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() + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) + : 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) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) ENTT_NOEXCEPT + : 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) ENTT_NOEXCEPT + : 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) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) ENTT_NOEXCEPT { + 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) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const ENTT_NOEXCEPT { + return calls.get_allocator(); + } + + /** + * @brief Instance type when it comes to connecting member functions. + * @tparam Class Type of class to which the member function belongs. + */ + template + using instance_type = Class *; + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const ENTT_NOEXCEPT { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto &&call: std::as_const(calls)) { + call(args...); + } + } + + /** + * @brief Collects return values from the listeners. + * + * The collector must expose a call operator with the following properties: + * + * * The return type is either `void` or such that it's convertible to + * `bool`. In the second case, a true value will stop the iteration. + * * The list of parameters is empty if `Ret` is `void`, otherwise it + * contains a single element such that `Ret` is convertible to it. + * + * @tparam Func Type of collector to use, if any. + * @param func A valid function object. + * @param args Arguments to use to invoke listeners. + */ + template + void collect(Func func, Args... args) const { + for(auto &&call: calls) { + if constexpr(std::is_void_v) { + if constexpr(std::is_invocable_r_v) { + call(args...); + if(func()) { break; } + } else { + call(args...); + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(call(args...))) { break; } + } else { + func(call(args...)); + } + } + } + } + +private: + 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() = default; + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const ENTT_NOEXCEPT { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal{}; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) ENTT_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) ENTT_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 ENTT_NOEXCEPT { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @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(); + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) ENTT_NOEXCEPT + : offset{}, + signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const ENTT_NOEXCEPT { + return signal->calls.empty(); + } + + /** + * @brief Returns a sink that connects before a given free function or an + * unbound member. + * @tparam Function A valid free function pointer. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before() { + delegate call{}; + call.template connect(); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a free function with payload + * or a bound member. + * @tparam Candidate Member or free function to look for. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &&value_or_instance) { + delegate call{}; + call.template connect(value_or_instance); + + const auto &calls = signal->calls; + const auto it = std::find(calls.cbegin(), calls.cend(), std::move(call)); + + sink other{*this}; + other.offset = calls.cend() - it; + return other; + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type &value_or_instance) { + return before(&value_or_instance); + } + + /** + * @brief Returns a sink that connects before a given instance or specific + * payload. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + * @return A properly initialized sink object. + */ + template + [[nodiscard]] sink before(Type *value_or_instance) { + sink other{*this}; + + if(value_or_instance) { + const auto &calls = signal->calls; + const auto it = std::find_if(calls.cbegin(), calls.cend(), [value_or_instance](const auto &delegate) { + return delegate.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 or an unbound member to a signal. + * + * The signal handler performs checks to avoid multiple connections for the + * same function. + * + * @tparam Candidate Function or member to connect to the signal. + * @return A properly initialized connection object. + */ + template + connection connect() { + disconnect(); + + delegate call{}; + call.template connect(); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(); + return {std::move(conn), signal}; + } + + /** + * @brief Connects a free function with payload or a bound member to a + * signal. + * + * The signal isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the signal. On the other side, the signal handler performs + * checks to avoid multiple connections for the same function.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the signal itself. + * + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&value_or_instance) { + disconnect(value_or_instance); + + delegate call{}; + call.template connect(value_or_instance); + signal->calls.insert(signal->calls.end() - offset, std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function or an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + */ + template + void disconnect() { + auto &calls = signal->calls; + delegate call{}; + call.template connect(); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects a free function with payload or a bound member from a + * signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &&value_or_instance) { + auto &calls = signal->calls; + delegate call{}; + call.template connect(value_or_instance); + calls.erase(std::remove(calls.begin(), calls.end(), std::move(call)), calls.end()); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + if(value_or_instance) { + auto &calls = signal->calls; + 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 + diff --git a/src/core/common_defs.h b/src/core/common_defs.h index 84a8806..7cad8a4 100644 --- a/src/core/common_defs.h +++ b/src/core/common_defs.h @@ -39,6 +39,8 @@ static_assert(sizeof(i64) == 8, "Expected i64 to be 8 bytes"); static_assert(sizeof(f32) == 4, "Expected f32 to be 4 bytes"); static_assert(sizeof(f64) == 8, "Expected f64 to be 8 bytes"); +typedef u64 LUUID; + // Platform detection #if defined(_WIN32) || defined(_WIN64) diff --git a/src/run_modes/editor/editor.cpp b/src/run_modes/editor/editor.cpp index ca42e8a..ac6c47f 100644 --- a/src/run_modes/editor/editor.cpp +++ b/src/run_modes/editor/editor.cpp @@ -8,6 +8,7 @@ #include "editor.h" +#include #include "panel_manager.h" #include #include @@ -25,7 +26,6 @@ // Tools #include "tools/map_editor/map_editor.h" -#include using namespace lunarium::gui; @@ -203,21 +203,18 @@ namespace editor { if (ImGui::MenuItem("New Project")) { - NFD::Guard nfdGuard; - - // auto-freeing memory - NFD::UniquePath outPath; + std::filesystem::path outPath; // prepare filters for the dialog - nfdfilteritem_t filterItem[1] = {{"Lunarium Project File", "lproj"}}; + const FileSystem::FilterItem filterItem[1] = {{"Lunarium Project File", "lproj"}}; // show the dialog - nfdresult_t result = NFD::SaveDialog(outPath, filterItem, 1); - if (result == NFD_OKAY) + FileSystem::DialogResult result = FileSystem::SaveFileDialog(outPath, filterItem, 1); + if (result == FileSystem::DialogResult::OK) { - std::filesystem::path the_path = outPath.get(); - Logger::Info(LogCat, "Generating new project at %s", the_path.string().c_str()); - OpRes result = mProject.GenerateProject(the_path.filename().string(), the_path.parent_path()); + //std::filesystem::path the_path = outPath.get(); + Logger::Info(LogCat, "Generating new project at %s", outPath.string().c_str()); + OpRes result = mProject.GenerateProject(outPath.filename().string(), outPath.parent_path()); if (Failed(result)) { Logger::Error(LogCat, "Could not create a new project: %s", result.Description); @@ -225,59 +222,54 @@ namespace editor else { ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))-> - SetAssetDirectory(the_path.parent_path() / std::filesystem::path("contents/assets")); + SetAssetDirectory(outPath.parent_path() / std::filesystem::path("contents/assets")); } } - else if (result == NFD_CANCEL) + else if (result == FileSystem::DialogResult::CANCEL) { Logger::Info(LogCat, "User cancelled project create"); } else { - Logger::Error(LogCat, "Error getting project file location: %s", NFD::GetError()); + Logger::Error(LogCat, "Error getting project file location: %s", FileSystem::GetError().c_str()); } } if (ImGui::MenuItem("Open Project")) { - NFD::Guard nfdGuard; - - // auto-freeing memory - NFD::UniquePath outPath; + std::filesystem::path outPath; // prepare filters for the dialog - nfdfilteritem_t filterItem[1] = {{"Lunarium Project File", "lproj"}}; + FileSystem::FilterItem filterItem[1] = {{"Lunarium Project File", "lproj"}}; // show the dialog - nfdresult_t result = NFD::OpenDialog(outPath, filterItem, 1); - if (result == NFD_OKAY) + FileSystem::DialogResult result = FileSystem::OpenFileDialog(outPath, filterItem, 1); + if (result == FileSystem::DialogResult::OK) { - std::filesystem::path the_path = outPath.get(); - - Logger::Info(LogCat, "Opening project: %s", the_path.string().c_str()); + Logger::Info(LogCat, "Opening project: %s", outPath.string().c_str()); // Open project at mpPath - if (Failed(mProject.LoadProject(the_path).LogIfFailed(Editor::LogCat))) + if (Failed(mProject.LoadProject(outPath).LogIfFailed(Editor::LogCat))) { - Logger::Error(LogCat, "Failed to load project: %s", the_path.string().c_str()); + Logger::Error(LogCat, "Failed to load project: %s", outPath.string().c_str()); } else { ((AssetBrowser*)mPanelManager.GetPanel(mPanels.AssetBrowser))-> - SetAssetDirectory(the_path.parent_path() / std::filesystem::path("contents/assets")); + SetAssetDirectory(outPath.parent_path() / std::filesystem::path("contents/assets")); } } - else if (result == NFD_CANCEL) + else if (result == FileSystem::DialogResult::CANCEL) { Logger::Info(LogCat, "User cancelled project open"); } else { - Logger::Error(LogCat, "Error getting project file location: %s", NFD::GetError()); + Logger::Error(LogCat, "Error getting project file location: %s", FileSystem::GetError().c_str()); } } diff --git a/src/run_modes/editor/tools/map_editor/map_editor.cpp b/src/run_modes/editor/tools/map_editor/map_editor.cpp index f859020..bce0060 100644 --- a/src/run_modes/editor/tools/map_editor/map_editor.cpp +++ b/src/run_modes/editor/tools/map_editor/map_editor.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include // Panels #include "panels/map_canvas.h" @@ -217,24 +217,19 @@ namespace lunarium { namespace editor ImGui::Separator(); if (ImGui::MenuItem("Import Tile Set")) { - NFD::Guard nfdGuard; - - // auto-freeing memory - NFD::UniquePath outPath; + std::filesystem::path outPath; // prepare filters for the dialog - nfdfilteritem_t filterItem[3] = {{"Image file", "png"}, {"Image File", "jpg"}, {"Image File", "jpeg"}}; + FileSystem::FilterItem filterItem[3] = {{"Image file", "png"}, {"Image File", "jpg"}, {"Image File", "jpeg"}}; // show the dialog - nfdresult_t result = NFD::OpenDialog(outPath, filterItem, 3); - if (result == NFD_OKAY) + FileSystem::DialogResult result = FileSystem::OpenFileDialog(outPath, filterItem, 3); + if (result == FileSystem::DialogResult::OK) { - std::filesystem::path the_path = outPath.get(); - - Logger::Info(Editor::LogCat, "Importing asset: %s", the_path.string().c_str()); + Logger::Info(Editor::LogCat, "Importing asset: %s", outPath.string().c_str()); uint64_t id = 0; - ContentManager::GetInstance().ImportFile(the_path, mpEditor->GetAssetBrowserLocation() / the_path.filename(), AssetType::EATYPE_TILE_SET, id).LogIfFailed(Editor::LogCat); + ContentManager::GetInstance().ImportFile(outPath, mpEditor->GetAssetBrowserLocation() / outPath.filename(), AssetType::EATYPE_TILE_SET, id).LogIfFailed(Editor::LogCat); TileSet* ts = (TileSet*)ContentManager::GetInstance().GetAsset(id); ts->SetTileSize({16, 16}); // NOTE: Hardcoding the tile size for testing @@ -248,7 +243,6 @@ namespace lunarium { namespace editor mpMap->AddTileSet(ts->GetTileSetID(), ts); ((TileSetView*)mPanelManager.GetPanel(mPanels.TileSetView))->AddTileSet(ts); } - } } diff --git a/src/utils/helpers.cpp b/src/utils/helpers.cpp index ff2ca2b..9bd8fff 100644 --- a/src/utils/helpers.cpp +++ b/src/utils/helpers.cpp @@ -8,8 +8,9 @@ #include "helpers.h" #include "logger.h" +#include + #include -#include #ifdef WIN32 #include @@ -62,6 +63,57 @@ namespace lunarium return std::filesystem::create_directory(path); } + FileSystem::DialogResult FileSystem::SaveFileDialog(std::filesystem::path& outPath, const FilterItem* filters, + u32 num_filters, const u8* default_path, const u8* default_name) + { + + NFD::Guard nfdGuard; + NFD::UniquePath outPath_intern; + DialogResult result = (DialogResult) NFD::SaveDialog(outPath_intern, (const nfdu8filteritem_t*) filters, (nfdfiltersize_t)num_filters, (const nfdu8char_t*)default_path, (const nfdu8char_t*)default_name); + + outPath = ""; + if (result == DialogResult::OK) + { + outPath = outPath_intern.get(); + } + return result; + } + + FileSystem::DialogResult FileSystem::OpenFileDialog(std::filesystem::path& outPath, const FilterItem* filters, + u32 num_filters, const u8* default_path) + { + NFD::Guard nfdGuard; + NFD::UniquePath outPath_intern; + DialogResult result = (DialogResult) NFD::OpenDialog(outPath_intern, (const nfdu8filteritem_t*) filters, + (nfdfiltersize_t)num_filters, (const nfdu8char_t*)default_path); + + outPath = ""; + if (result == DialogResult::OK) + { + outPath = outPath_intern.get(); + } + return result; + } + + FileSystem::DialogResult FileSystem::PickDirectory(std::filesystem::path& outPath, const u8* default_path) + { + + NFD::Guard nfdGuard; + NFD::UniquePath outPath_intern; + DialogResult result = (FileSystem::DialogResult) NFD::PickFolder(outPath_intern, (const nfdu8char_t*)default_path); + outPath = ""; + if (result == DialogResult::OK) + { + outPath = outPath_intern.get(); + } + return result; + } + + std::string FileSystem::GetError() + { + return std::string(NFD::GetError()); + } + //////////////////////////////////////////////////////////// // MATH FUNCTIONS //////////////////////////////////////////////////////////// diff --git a/src/utils/helpers.h b/src/utils/helpers.h index fa84b6e..89ef6ad 100644 --- a/src/utils/helpers.h +++ b/src/utils/helpers.h @@ -9,10 +9,12 @@ #ifndef HELPERS_H_ #define HELPERS_H_ +#include #include #include #include #include +#include namespace lunarium { @@ -28,6 +30,30 @@ namespace lunarium public: static std::vector GetFilesInDirectory(std::string path); static bool MakeDir(std::string path); + + + // Dialog Wrappers + enum DialogResult + { + ERROR, + OK, + CANCEL, + }; + + struct FilterItem + { + const char* name; + const char* spec; + }; + + static DialogResult SaveFileDialog(std::filesystem::path& outPath, const FilterItem* filters = nullptr, + u32 num_filters = 0, const u8* default_path = nullptr, const u8* default_name = nullptr); + + static DialogResult OpenFileDialog(std::filesystem::path& outPath, const FilterItem* filters = nullptr, + u32 num_filters = 0, const u8* default_path = nullptr); + + static DialogResult PickDirectory(std::filesystem::path& outPath, const u8* default_path = nullptr); + static std::string GetError(); }; class Math diff --git a/src/utils/uuid.cpp b/src/utils/uuid.cpp new file mode 100644 index 0000000..1c73668 --- /dev/null +++ b/src/utils/uuid.cpp @@ -0,0 +1,19 @@ +/****************************************************************************** +* File - uuid.cpp +* Author - Joey Pollack +* Date - 2022/05/23 (y/m/d) +* Mod Date - 2022/05/23 (y/m/d) +* Description - Generates 64 bit uuids +******************************************************************************/ + +#include "uuid.h" +#include + +namespace lunarium +{ + std::mt19937_64 UUID::mt64(time(nullptr)); + LUUID UUID::GetID() + { + return mt64(); + } +} \ No newline at end of file diff --git a/src/utils/uuid.h b/src/utils/uuid.h new file mode 100644 index 0000000..8c6c9f9 --- /dev/null +++ b/src/utils/uuid.h @@ -0,0 +1,28 @@ +/****************************************************************************** +* File - uuid.h +* Author - Joey Pollack +* Date - 2022/05/23 (y/m/d) +* Mod Date - 2022/05/23 (y/m/d) +* Description - Generates 64 bit uuids +******************************************************************************/ + +#ifndef LUNARIUM_UUID_H_ +#define LUNARIUM_UUID_H_ + +#include +#include + +namespace lunarium +{ + class UUID + { + public: + static LUUID GetID(); + + private: + static std::mt19937_64 mt64; + }; +} + + +#endif // LUNARIUM_UUID_H_ \ No newline at end of file diff --git a/src/world/components.h b/src/world/components.h new file mode 100644 index 0000000..f848800 --- /dev/null +++ b/src/world/components.h @@ -0,0 +1,24 @@ +/****************************************************************************** +* File - components.h +* Author - Joey Pollack +* Date - 2022/05/24 (y/m/d) +* Mod Date - 2022/05/24 (y/m/d) +* Description - ECS component declarations +******************************************************************************/ + +#ifndef LUNARIUM_COMPONENTS_H_ +#define LUNARIUM_COMPONENTS_H_ + +#include + +#include + +namespace lunarium +{ + struct LabelComponent + { + std::string Label + }; +} + +#endif // LUNARIUM_COMPONENTS_H_ \ No newline at end of file diff --git a/src/world/entity.cpp b/src/world/entity.cpp new file mode 100644 index 0000000..1daa44a --- /dev/null +++ b/src/world/entity.cpp @@ -0,0 +1,18 @@ +/****************************************************************************** +* File - entity.cpp +* Author - Joey Pollack +* Date - 2022/05/23 (y/m/d) +* Mod Date - 2022/05/23 (y/m/d) +* Description - Provides functionality to work with world entities +******************************************************************************/ + +#include "entity.h" + +namespace lunarium +{ + Entity::Entity(entt::registry* r) + : mID(entt::null), mpRegistry(r) + { + + } +} \ No newline at end of file diff --git a/src/world/entity.h b/src/world/entity.h new file mode 100644 index 0000000..3b8b0c4 --- /dev/null +++ b/src/world/entity.h @@ -0,0 +1,27 @@ +/****************************************************************************** +* File - entity.h +* Author - Joey Pollack +* Date - 2022/05/23 (y/m/d) +* Mod Date - 2022/05/23 (y/m/d) +* Description - Provides functionality to work with world entities +******************************************************************************/ + +#ifndef LUNARIUM_ENTITY_H_ +#define LUNARIUM_ENTITY_H_ + +#include + +namespace lunarium +{ + class Entity + { + public: + Entity(entt::registry* r); + + private: + entt::entity mID; + entt::registry* mpRegistry; + }; +} + +#endif // LUNARIUM_ENTITY_H_ \ No newline at end of file diff --git a/src/world/world.h b/src/world/world.h index 16c34b3..1164bc6 100644 --- a/src/world/world.h +++ b/src/world/world.h @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -23,13 +24,12 @@ namespace lunarium class Graphics; class Image; class Script; + class Camera; + class GameObject; } namespace lunarium { - class Camera; - class GameObject; - class World { public: @@ -52,12 +52,6 @@ namespace lunarium void Render(Graphics* pGraphics) const; void RenderToTexture(Graphics* pGraphics, Image* pTexture) const; - // Game objects should probably be added/removed from the regions directly - // No World-wide game objects - // Unless I can think of a good reason why they would be useful - // void AddGameObject(GameObject* pObj); - // bool RemoveGameObject(GameObject* pObj); - OpRes SetRegion(Region* region, Vec2i at); Region* GetRegion(Vec2i at); bool RemoveRegion(Vec2i at); @@ -67,6 +61,7 @@ namespace lunarium private: + entt::registry mECSRegistry; Camera* mpCamera; std::vector mWorldObjects; lunarium::Script* mpWorldScript;