From 6a3be919a52c545109717dd23d435bcd5ae60f37 Mon Sep 17 00:00:00 2001 From: Jeongseok Lee Date: Tue, 19 Mar 2024 08:19:18 -0700 Subject: [PATCH] [v7] wip --- .github/workflows/ci_macos.yml | 1 + .github/workflows/ci_windows.yml | 2 + .github/workflows/publish_dartpy.yml | 1 + Brewfile | 1 + CMakeLists.txt | 1 + cmake/DARTFindDependencies.cmake | 6 + cmake/DARTFindEnTT.cmake | 9 + dart/config.hpp.in | 1 + dart/external/CMakeLists.txt | 3 + dart/external/entt/CMakeLists.txt | 40 + dart/external/entt/entt.hpp | 89894 +++++++++++++++++++ dart/v7/comps/all.hpp | 52 + dart/v7/ecs/context.cpp | 48 + dart/v7/ecs/context.hpp | 50 + dart/v7/ecs/entity.hpp | 41 + dart/v7/ecs/include_entt.hpp | 41 + dart/v7/ecs/system.cpp | 39 + dart/v7/ecs/system.hpp | 46 + dart/v7/ecs/system_graph.cpp | 42 + dart/v7/ecs/system_graph.hpp | 45 + dart/v7/rigid_body.cpp | 84 + dart/v7/rigid_body.hpp | 64 + dart/v7/world.cpp | 131 + dart/v7/world.hpp | 94 + dart/v7/world_object.cpp | 65 + dart/v7/world_object.hpp | 62 + docs/readthedocs/developer_guide/build.rst | 4 +- setup.py | 4 + tests/unit/common/test_ecs.cpp | 94 + tests/unit/v7/test_world.cpp | 68 + 30 files changed, 91031 insertions(+), 2 deletions(-) create mode 100644 cmake/DARTFindEnTT.cmake create mode 100644 dart/external/entt/CMakeLists.txt create mode 100644 dart/external/entt/entt.hpp create mode 100644 dart/v7/comps/all.hpp create mode 100644 dart/v7/ecs/context.cpp create mode 100644 dart/v7/ecs/context.hpp create mode 100644 dart/v7/ecs/entity.hpp create mode 100644 dart/v7/ecs/include_entt.hpp create mode 100644 dart/v7/ecs/system.cpp create mode 100644 dart/v7/ecs/system.hpp create mode 100644 dart/v7/ecs/system_graph.cpp create mode 100644 dart/v7/ecs/system_graph.hpp create mode 100644 dart/v7/rigid_body.cpp create mode 100644 dart/v7/rigid_body.hpp create mode 100644 dart/v7/world.cpp create mode 100644 dart/v7/world.hpp create mode 100644 dart/v7/world_object.cpp create mode 100644 dart/v7/world_object.hpp create mode 100644 tests/unit/common/test_ecs.cpp create mode 100644 tests/unit/v7/test_world.cpp diff --git a/.github/workflows/ci_macos.yml b/.github/workflows/ci_macos.yml index 0df031caaa3bf..8f680a3dda2aa 100644 --- a/.github/workflows/ci_macos.yml +++ b/.github/workflows/ci_macos.yml @@ -34,6 +34,7 @@ jobs: COMPILER: clang BUILD_TYPE: ${{ matrix.build_type }} BUILD_DARTPY: ON + DART_USE_SYSTEM_ENTT: ON DART_USE_SYSTEM_IMGUI: OFF IN_CI: ON ENABLE_SIMD: ${{ matrix.enable_simd }} diff --git a/.github/workflows/ci_windows.yml b/.github/workflows/ci_windows.yml index ba229e9725c38..7f4e35839c237 100644 --- a/.github/workflows/ci_windows.yml +++ b/.github/workflows/ci_windows.yml @@ -41,6 +41,7 @@ jobs: pkgs: > assimp eigen3 + entt fcl fmt spdlog @@ -78,6 +79,7 @@ jobs: -DDART_MSVC_DEFAULT_OPTIONS=ON ^ -DDART_VERBOSE=ON ^ -DBUILD_SHARED_LIBS=${{ matrix.build_shared_libs }} ^ + -DDART_USE_SYSTEM_ENTT=ON ^ -DDART_USE_SYSTEM_IMGUI=OFF ^ || exit /b cmake ^ diff --git a/.github/workflows/publish_dartpy.yml b/.github/workflows/publish_dartpy.yml index 2dec358a14891..d072583e872a2 100644 --- a/.github/workflows/publish_dartpy.yml +++ b/.github/workflows/publish_dartpy.yml @@ -103,6 +103,7 @@ jobs: assimp ccd eigen3 + entt fcl fmt spdlog diff --git a/Brewfile b/Brewfile index 83201c5ab8aae..0574ee13e11c0 100644 --- a/Brewfile +++ b/Brewfile @@ -14,6 +14,7 @@ brew 'octomap' brew 'ode' #brew 'open-scene-graph' # disabled until 3.7.0 is released brew 'pagmo' +brew 'skypjack/entt/entt' brew 'spdlog' brew 'tinyxml2' brew 'urdfdom' diff --git a/CMakeLists.txt b/CMakeLists.txt index 7e2ef2722ec21..cdfcce24264fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ dart_option(DART_FAST_DEBUG "Add -O1 option for DEBUG mode build" OFF) # See: https://medium.com/@alasher/colored-c-compiler-output-with-ninja-clang-gcc-10bfe7f2b949 dart_option(DART_FORCE_COLORED_OUTPUT "Always produce ANSI-colored output (GNU/Clang only)." OFF) +dart_option(DART_USE_SYSTEM_ENTT "Use system EnTT" OFF) dart_option(DART_USE_SYSTEM_IMGUI "Use system ImGui" OFF) dart_option(DART_IN_CI "Indicate building DART as part of CI" OFF) diff --git a/cmake/DARTFindDependencies.cmake b/cmake/DARTFindDependencies.cmake index 1c22898d45be7..482fe620f27af 100644 --- a/cmake/DARTFindDependencies.cmake +++ b/cmake/DARTFindDependencies.cmake @@ -17,6 +17,12 @@ dart_check_required_package(fmt "libfmt") dart_find_package(Eigen3) dart_check_required_package(EIGEN3 "eigen3") +# Entt +if(DART_USE_SYSTEM_ENTT) + dart_find_package(EnTT) + dart_check_required_package(EnTT "EnTT") +endif() + # FCL dart_find_package(fcl) dart_check_required_package(fcl "fcl") diff --git a/cmake/DARTFindEnTT.cmake b/cmake/DARTFindEnTT.cmake new file mode 100644 index 0000000000000..03978ca2e1fdd --- /dev/null +++ b/cmake/DARTFindEnTT.cmake @@ -0,0 +1,9 @@ +# Copyright (c) 2011-2024, The DART development contributors +# All rights reserved. +# +# The list of contributors can be found at: +# https://github.com/dartsim/dart/blob/main/LICENSE +# +# This file is provided under the "BSD-style" License + +find_package(EnTT QUIET CONFIG) diff --git a/dart/config.hpp.in b/dart/config.hpp.in index 7fefbcf7c77d7..34938e16f72d3 100644 --- a/dart/config.hpp.in +++ b/dart/config.hpp.in @@ -97,6 +97,7 @@ // BULLET_DEFINITIONS or generate a #cmakedefine header. #cmakedefine BT_USE_DOUBLE_PRECISION +#cmakedefine01 DART_USE_SYSTEM_ENTT #cmakedefine01 DART_USE_SYSTEM_IMGUI #cmakedefine01 DART_BUILD_PROFILE diff --git a/dart/external/CMakeLists.txt b/dart/external/CMakeLists.txt index 02c95eacfe27f..c892db69e3e65 100644 --- a/dart/external/CMakeLists.txt +++ b/dart/external/CMakeLists.txt @@ -1,4 +1,7 @@ add_subdirectory(convhull_3d) +if(NOT DART_USE_SYSTEM_ENTT) + add_subdirectory(entt) +endif() if(NOT DART_USE_SYSTEM_IMGUI) add_subdirectory(imgui) endif() diff --git a/dart/external/entt/CMakeLists.txt b/dart/external/entt/CMakeLists.txt new file mode 100644 index 0000000000000..86e35d1b9fc5b --- /dev/null +++ b/dart/external/entt/CMakeLists.txt @@ -0,0 +1,40 @@ +# Copyright (c) The DART development contributors +# All rights reserved. +# +# The list of contributors can be found at: +# https://github.com/dartsim/dart/blob/master/LICENSE +# +# This file is provided under the following "BSD-style" License: +# Redistribution and use in source and binary forms, with or +# without modification, are permitted provided that the following +# conditions are met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND +# CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED +# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. + +if(DART_USE_SYSTEM_ENTT) + return() +endif() + +# TODO: Download file from github +install( + FILES entt.hpp + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dart/external/entt + COMPONENT headers +) diff --git a/dart/external/entt/entt.hpp b/dart/external/entt/entt.hpp new file mode 100644 index 0000000000000..3f06ce52ad8c9 --- /dev/null +++ b/dart/external/entt/entt.hpp @@ -0,0 +1,89894 @@ +// IWYU pragma: begin_exports +// #include "config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "config/macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + +// #include "config/version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + +// #include "container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } + + *pre = std::move(value); + } + } + } +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + 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) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::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.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_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 && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] 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> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "core/family.hpp" +#ifndef ENTT_CORE_FAMILY_HPP +#define ENTT_CORE_FAMILY_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Dynamic identifier generator. + * + * Utility class template that can be used to assign unique identifiers to types + * at runtime. Use different specializations to create separate sets of + * identifiers. + */ +template +class family { + inline static ENTT_MAYBE_ATOMIC(id_type) identifier{}; + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + // at the time I'm writing, clang crashes during compilation if auto is used instead of family_type + inline static const value_type value = identifier++; +}; + +} // namespace entt + +#endif + +// #include "core/hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + +// #include "core/ident.hpp" +#ifndef ENTT_CORE_IDENT_HPP +#define ENTT_CORE_IDENT_HPP + +#include +#include +#include +// #include "fwd.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Type integral identifiers. + * @tparam Type List of types for which to generate identifiers. + */ +template +class ident { + template + [[nodiscard]] static constexpr id_type get(std::index_sequence) noexcept { + static_assert((std::is_same_v || ...), "Invalid type"); + return (0 + ... + (std::is_same_v...>>> ? id_type{Index} : id_type{})); + } + +public: + /*! @brief Unsigned integer type. */ + using value_type = id_type; + + /*! @brief Statically generated unique identifier for the given type. */ + template + static constexpr value_type value = get>(std::index_sequence_for{}); +}; + +} // namespace entt + +#endif + +// #include "core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "core/monostate.hpp" +#ifndef ENTT_CORE_MONOSTATE_HPP +#define ENTT_CORE_MONOSTATE_HPP + +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Minimal implementation of the monostate pattern. + * + * A minimal, yet complete configuration system built on top of the monostate + * pattern. Thread safe by design, it works only with basic types like `int`s or + * `bool`s.
+ * Multiple types and therefore more than one value can be associated with a + * single key. Because of this, users must pay attention to use the same type + * both during an assignment and when they try to read back their data. + * Otherwise, they can incur in unexpected results. + */ +template +struct monostate { + /** + * @brief Assigns a value of a specific type to a given key. + * @tparam Type Type of the value to assign. + * @param val User data to assign to the given key. + */ + template + void operator=(Type val) const noexcept { + value = val; + } + + /** + * @brief Gets a value of a specific type for a given key. + * @tparam Type Type of the value to get. + * @return Stored value, if any. + */ + template + operator Type() const noexcept { + return value; + } + +private: + template + inline static ENTT_MAYBE_ATOMIC(Type) value{}; +}; + +/** + * @brief Helper variable template. + * @tparam Value Value used to differentiate between different variables. + */ +template +inline monostate monostate_v = {}; + +} // namespace entt + +#endif + +// #include "core/tuple.hpp" +#ifndef ENTT_CORE_TUPLE_HPP +#define ENTT_CORE_TUPLE_HPP + +#include +#include +#include + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct is_tuple_impl: std::false_type {}; + +template +struct is_tuple_impl>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is a + * tuple, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_tuple: internal::is_tuple_impl> {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_tuple_v = is_tuple::value; + +/** + * @brief Utility function to unwrap tuples of a single element. + * @tparam Type Tuple type of any sizes. + * @param value A tuple object of the given type. + * @return The tuple itself if it contains more than one element, the first + * element otherwise. + */ +template +constexpr decltype(auto) unwrap_tuple(Type &&value) noexcept { + if constexpr(std::tuple_size_v> == 1u) { + return std::get<0>(std::forward(value)); + } else { + return std::forward(value); + } +} + +/** + * @brief Utility class to forward-and-apply tuple objects. + * @tparam Func Type of underlying invocable object. + */ +template +struct forward_apply: private Func { + /** + * @brief Constructs a forward-and-apply object. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + constexpr forward_apply(Args &&...args) noexcept(std::is_nothrow_constructible_v) + : Func{std::forward(args)...} {} + + /** + * @brief Forwards and applies the arguments with the underlying function. + * @tparam Type Tuple-like type to forward to the underlying function. + * @param args Parameters to forward to the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Type &&args) noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Type &&args) const noexcept(noexcept(std::apply(std::declval(), args))) { + return std::apply(static_cast(*this), std::forward(args)); + } +}; + +/** + * @brief Deduction guide. + * @tparam Func Type of underlying invocable object. + */ +template +forward_apply(Func) -> forward_apply>>; + +} // namespace entt + +#endif + +// #include "core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "entity/component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_ENTITY_FWD_HPP +#define ENTT_ENTITY_FWD_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @brief Default entity identifier. */ +enum class entity : id_type {}; + +/*! @brief Storage 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 Swap-only deletion policy. */ + swap_only = 2u +}; + +template> +class basic_sparse_set; + +template, typename = void> +class basic_storage; + +template +class basic_sigh_mixin; + +template> +class basic_registry; + +template +class basic_view; + +template> +class basic_runtime_view; + +template +class basic_group; + +template> +class basic_observer; + +template +class basic_organizer; + +template +struct basic_handle; + +template +class basic_snapshot; + +template +class basic_snapshot_loader; + +template +class basic_continuous_loader; + +/*! @brief Alias declaration for the most common use case. */ +using sparse_set = basic_sparse_set<>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Type of objects assigned to the entities. + */ +template +using storage = basic_storage; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Type Underlying storage type. + */ +template +using sigh_mixin = basic_sigh_mixin>; + +/*! @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. */ +using runtime_view = basic_runtime_view; + +/*! @brief Alias declaration for the most common use case. */ +using const_runtime_view = basic_runtime_view; + +/** + * @brief Alias for exclusion lists. + * @tparam Type List of types. + */ +template +struct exclude_t final: type_list { + /*! @brief Default constructor. */ + explicit constexpr exclude_t() {} +}; + +/** + * @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 final: type_list { + /*! @brief Default constructor. */ + explicit constexpr get_t() {} +}; + +/** + * @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 final: type_list { + /*! @brief Default constructor. */ + explicit constexpr owned_t() {} +}; + +/** + * @brief Variable template for lists of owned components. + * @tparam Type List of types. + */ +template +inline constexpr owned_t owned{}; + +/** + * @brief Applies a given _function_ to a get list and generate a new list. + * @tparam Type Types provided by the get list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting get list after applying the transform function. */ + using type = get_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an exclude list and generate a new list. + * @tparam Type Types provided by the exclude list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting exclude list after applying the transform function. */ + using type = exclude_t::type...>; +}; + +/** + * @brief Applies a given _function_ to an owned list and generate a new list. + * @tparam Type Types provided by the owned list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting owned list after applying the transform function. */ + using type = owned_t::type...>; +}; + +/** + * @brief Provides a common way to define storage types. + * @tparam Type Storage value type. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template, typename = void> +struct storage_type { + /*! @brief Type-to-storage conversion result. */ + using type = sigh_mixin>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_type_t = typename storage_type::type; + +/** + * Type-to-storage conversion utility that preserves constness. + * @tparam Type Storage value type, eventually const. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template>> +struct storage_for { + /*! @brief Type-to-storage conversion result. */ + using type = constness_as_t, Entity, Allocator>, Type>; +}; + +/** + * @brief Helper type. + * @tparam Args Arguments to forward. + */ +template +using storage_for_t = typename storage_for::type; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template> +using view = basic_view, type_list_transform_t>; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +using group = basic_group, type_list_transform_t, type_list_transform_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // 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" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +// waiting for C++20 and std::popcount +template +constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + 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; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + 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; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + return (static_cast(to_integral(value) >> length) & version_mask); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { + return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { + return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "entity/group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" + +// #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 */ +namespace internal { + +// waiting for C++20 and std::popcount +template +constexpr int popcount(Type value) noexcept { + return value ? (int(value & 1) + popcount(value >> 1)) : 0; +} + +template +struct entt_traits; + +template +struct entt_traits>> + : entt_traits> { + using value_type = Type; +}; + +template +struct entt_traits>> + : entt_traits { + using value_type = Type; +}; + +template<> +struct entt_traits { + using value_type = std::uint32_t; + + 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; +}; + +template<> +struct entt_traits { + using value_type = std::uint64_t; + + 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; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common basic entity traits implementation. + * @tparam Traits Actual entity traits to use. + */ +template +class basic_entt_traits { + static constexpr auto length = internal::popcount(Traits::entity_mask); + + static_assert(Traits::entity_mask && ((typename Traits::entity_type{1} << length) == (Traits::entity_mask + 1)), "Invalid entity mask"); + static_assert((typename Traits::entity_type{1} << internal::popcount(Traits::version_mask)) == (Traits::version_mask + 1), "Invalid version mask"); + +public: + /*! @brief Value type. */ + using value_type = typename Traits::value_type; + /*! @brief Underlying entity type. */ + using entity_type = typename Traits::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename Traits::version_type; + + /*! @brief Entity mask size. */ + static constexpr entity_type entity_mask = Traits::entity_mask; + /*! @brief Version mask size */ + static constexpr entity_type version_mask = Traits::version_mask; + + /** + * @brief Converts an entity to its underlying type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ + [[nodiscard]] static constexpr entity_type to_integral(const value_type value) noexcept { + return static_cast(value); + } + + /** + * @brief Returns the entity part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ + [[nodiscard]] static constexpr entity_type to_entity(const value_type value) noexcept { + return (to_integral(value) & entity_mask); + } + + /** + * @brief Returns the version part once converted to the underlying type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ + [[nodiscard]] static constexpr version_type to_version(const value_type value) noexcept { + return (static_cast(to_integral(value) >> length) & version_mask); + } + + /** + * @brief Returns the successor of a given identifier. + * @param value The identifier of which to return the successor. + * @return The successor of the given identifier. + */ + [[nodiscard]] static constexpr value_type next(const value_type value) noexcept { + const auto vers = to_version(value) + 1; + return construct(to_integral(value), static_cast(vers + (vers == version_mask))); + } + + /** + * @brief Constructs an identifier from its parts. + * + * If the version part is not provided, a tombstone is returned.
+ * If the entity part is not provided, a null identifier is returned. + * + * @param entity The entity part of the identifier. + * @param version The version part of the identifier. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type construct(const entity_type entity, const version_type version) noexcept { + return value_type{(entity & entity_mask) | (static_cast(version & version_mask) << length)}; + } + + /** + * @brief Combines two identifiers in a single one. + * + * The returned identifier is a copy of the first element except for its + * version, which is taken from the second element. + * + * @param lhs The identifier from which to take the entity part. + * @param rhs The identifier from which to take the version part. + * @return A properly constructed identifier. + */ + [[nodiscard]] static constexpr value_type combine(const entity_type lhs, const entity_type rhs) noexcept { + return value_type{(lhs & entity_mask) | (rhs & (version_mask << length))}; + } +}; + +/** + * @brief Entity traits. + * @tparam Type Type of identifier. + */ +template +struct entt_traits: basic_entt_traits> { + /*! @brief Base type. */ + using base_type = basic_entt_traits>; + /*! @brief Page size, default is `ENTT_SPARSE_PAGE`. */ + static constexpr std::size_t page_size = ENTT_SPARSE_PAGE; +}; + +/** + * @brief Converts an entity to its underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the given value. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_integral(const Entity value) noexcept { + return entt_traits::to_integral(value); +} + +/** + * @brief Returns the entity part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the entity part. + */ +template +[[nodiscard]] constexpr typename entt_traits::entity_type to_entity(const Entity value) noexcept { + return entt_traits::to_entity(value); +} + +/** + * @brief Returns the version part once converted to the underlying type. + * @tparam Entity The value type. + * @param value The value to convert. + * @return The integral representation of the version part. + */ +template +[[nodiscard]] constexpr typename entt_traits::version_type to_version(const Entity value) noexcept { + return entt_traits::to_version(value); +} + +/*! @brief Null object for all identifiers. */ +struct null_t { + /** + * @brief Converts the null object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The null representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const null_t other) const noexcept { + return true; + } + + /** + * @brief Compares two null objects. + * @param other A null object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const null_t other) const noexcept { + return false; + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_entity(entity) == traits_type::to_entity(*this); + } + + /** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const null_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a null object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A null object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const null_t other) noexcept { + return !(other == entity); +} + +/*! @brief Tombstone object for all identifiers. */ +struct tombstone_t { + /** + * @brief Converts the tombstone object to identifiers of any type. + * @tparam Entity Type of identifier. + * @return The tombstone representation for the given type. + */ + template + [[nodiscard]] constexpr operator Entity() const noexcept { + using traits_type = entt_traits; + constexpr auto value = traits_type::construct(traits_type::entity_mask, traits_type::version_mask); + return value; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return True in all cases. + */ + [[nodiscard]] constexpr bool operator==([[maybe_unused]] const tombstone_t other) const noexcept { + return true; + } + + /** + * @brief Compares two tombstone objects. + * @param other A tombstone object. + * @return False in all cases. + */ + [[nodiscard]] constexpr bool operator!=([[maybe_unused]] const tombstone_t other) const noexcept { + return false; + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return False if the two elements differ, true otherwise. + */ + template + [[nodiscard]] constexpr bool operator==(const Entity entity) const noexcept { + using traits_type = entt_traits; + return traits_type::to_version(entity) == traits_type::to_version(*this); + } + + /** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @return True if the two elements differ, false otherwise. + */ + template + [[nodiscard]] constexpr bool operator!=(const Entity entity) const noexcept { + return !(entity == *this); + } +}; + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return False if the two elements differ, true otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const Entity entity, const tombstone_t other) noexcept { + return other.operator==(entity); +} + +/** + * @brief Compares a tombstone object and an identifier of any type. + * @tparam Entity Type of identifier. + * @param entity Identifier with which to compare. + * @param other A tombstone object yet to be converted. + * @return True if the two elements differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const Entity entity, const tombstone_t other) noexcept { + return !(other == entity); +} + +/** + * @brief Compile-time constant for null entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the null + * entity and any other identifier. + */ +inline constexpr null_t null{}; + +/** + * @brief Compile-time constant for tombstone entities. + * + * There exist implicit conversions from this variable to identifiers of any + * allowed type. Similarly, there exist comparison operators between the + * tombstone entity and any other identifier. + */ +inline constexpr tombstone_t tombstone{}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" +#ifndef ENTT_CORE_ALGORITHM_HPP +#define ENTT_CORE_ALGORITHM_HPP + +#include +#include +#include +#include +#include +// #include "utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Function object to wrap `std::sort` in a class type. + * + * Unfortunately, `std::sort` cannot be passed as template argument to a class + * template or a function template.
+ * This class fills the gap by wrapping some flavors of `std::sort` in a + * function object. + */ +struct std_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @tparam Args Types of arguments to forward to the sort function. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + * @param args Arguments to forward to the sort function, if any. + */ + template, typename... Args> + void operator()(It first, It last, Compare compare = Compare{}, Args &&...args) const { + std::sort(std::forward(args)..., std::move(first), std::move(last), std::move(compare)); + } +}; + +/*! @brief Function object for performing insertion sort. */ +struct insertion_sort { + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given binary comparison function. + * + * @tparam It Type of random access iterator. + * @tparam Compare Type of comparison function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param compare A valid comparison function object. + */ + template> + void operator()(It first, It last, Compare compare = Compare{}) const { + if(first < last) { + for(auto it = first + 1; it < last; ++it) { + auto value = std::move(*it); + auto pre = it; + + for(; pre > first && compare(value, *(pre - 1)); --pre) { + *pre = std::move(*(pre - 1)); + } + + *pre = std::move(value); + } + } + } +}; + +/** + * @brief Function object for performing LSD radix sort. + * @tparam Bit Number of bits processed per pass. + * @tparam N Maximum number of bits to sort. + */ +template +struct radix_sort { + static_assert((N % Bit) == 0, "The maximum number of bits to sort must be a multiple of the number of bits processed per pass"); + + /** + * @brief Sorts the elements in a range. + * + * Sorts the elements in a range using the given _getter_ to access the + * actual data to be sorted. + * + * This implementation is inspired by the online book + * [Physically Based Rendering](http://www.pbr-book.org/3ed-2018/Primitives_and_Intersection_Acceleration/Bounding_Volume_Hierarchies.html#RadixSort). + * + * @tparam It Type of random access iterator. + * @tparam Getter Type of _getter_ function object. + * @param first An iterator to the first element of the range to sort. + * @param last An iterator past the last element of the range to sort. + * @param getter A valid _getter_ function object. + */ + template + void operator()(It first, It last, Getter getter = Getter{}) const { + if(first < last) { + 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) { + constexpr auto mask = (1 << Bit) - 1; + constexpr auto buckets = 1 << Bit; + + std::size_t index[buckets]{}; + std::size_t count[buckets]{}; + + for(auto it = from; it != to; ++it) { + ++count[(getter(*it) >> start) & mask]; + } + + for(std::size_t pos{}, end = buckets - 1u; pos < end; ++pos) { + index[pos + 1u] = index[pos] + count[pos]; + } + + for(auto it = from; it != to; ++it) { + out[index[(getter(*it) >> start) & mask]++] = std::move(*it); + } + }; + + for(std::size_t pass = 0; pass < (passes & ~1); pass += 2) { + part(first, last, aux.begin(), pass * Bit); + part(aux.begin(), aux.end(), first, (pass + 1) * Bit); + } + + if constexpr(passes & 1) { + part(first, last, aux.begin(), (passes - 1) * Bit); + std::move(aux.begin(), aux.end(), first); + } + } + } +}; + +} // namespace entt + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::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.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_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 && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] 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> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{std::addressof(ref)}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return packed->data()[index() - value]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return packed->data() + index(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 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. + * @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 underlying_type = typename entt_traits::entity_type; + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t from, const std::size_t to) { + auto &lhs = packed[from]; + auto &rhs = packed[to]; + + sparse_ref(lhs) = traits_type::combine(static_cast(to), traits_type::to_integral(lhs)); + sparse_ref(rhs) = traits_type::combine(static_cast(from), traits_type::to_integral(rhs)); + + std::swap(lhs, rhs); + } + + underlying_type policy_to_head() { + return traits_type::entity_mask * (mode != deletion_policy::swap_only); + } + +private: + virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = static_cast(index(*it)); + bump(traits_type::next(*it)); + swap_at(pos, static_cast(head -= (pos < head))); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = traits_type::combine(std::exchange(head, entt), tombstone); + } + +protected: + /** + * @brief Erases entities from a sparse set. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null)) { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @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) { + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null) && !force_back) { + pos = static_cast(head); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(head, traits_type::to_integral(entt)); + head = traits_type::to_entity(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available"); + bump(entt); + } + + if(force_back) { + pos = static_cast(head++); + swap_at(static_cast(traits_type::to_entity(elem)), pos); + } + + break; + } + + return --(end() - pos); + } + +public: + /*! @brief Entity traits. */ + using traits_type = entt_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @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 = std::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 elem 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 &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + info{&elem}, + mode{pol}, + head{policy_to_head()} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + + release_sparse_pages(); + sparse = std::move(other.sparse); + packed = std::move(other.packed); + info = other.info; + mode = other.mode; + head = std::exchange(other.head, policy_to_head()); + 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(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns the head of the free list, if any. + * @return The head of the free list. + */ + [[nodiscard]] size_type free_list() const noexcept { + return static_cast(head); + } + + /** + * @brief Sets the head of the free list, if possible. + * @param len The value to use as the new head of the free list. + */ + void free_list(const size_type len) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value"); + head = static_cast(len); + } + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + virtual void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void shrink_to_fit() { + packed.shrink_to_fit(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::page_size; + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * 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]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /*! @copydoc begin Useful only in case of swap-only policy. */ + [[nodiscard]] iterator begin(int) const noexcept { + return (mode == deletion_policy::swap_only) ? (end() - static_cast(head)) : begin(); + } + + /*! @copydoc cbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cbegin(int) const noexcept { + return begin(0); + } + + /*! @copydoc end Useful only in case of swap-only policy. */ + [[nodiscard]] iterator end(int) const noexcept { + return end(); + } + + /*! @copydoc cend Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cend(int) const noexcept { + return end(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rbegin(int) const noexcept { + return std::make_reverse_iterator(end(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crbegin(int) const noexcept { + return rbegin(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rend(int) const noexcept { + return std::make_reverse_iterator(begin(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crend(int) const noexcept { + return rend(0); + } + + /** + * @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]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return static_cast(traits_type::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. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept { + return pos < packed.size() ? packed[pos] : null; + } + + /** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { + ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(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 elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, (mode == deletion_policy::swap_only), elem); + } + + /** + * @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 push(It first, It last) { + for(auto it = first; it != last; ++it) { + try_emplace(*it, true); + } + + return first == last ? end() : find(*first); + } + + /** + * @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. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + for(; from && packed[from - 1u] == tombstone; --from) {} + underlying_type pos = std::exchange(head, traits_type::entity_mask); + + while(pos != traits_type::to_entity(null)) { + if(const auto to = static_cast(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + from, packed.end()); + } + } + + /** + * @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) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, 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((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + 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_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::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) { + sort_n(static_cast(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @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 sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + + for(auto it = begin(0); it.index() && first != last; ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + } + + /** + * @copybrief sort_as + * @param other The sparse sets that imposes the order of the entities. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) { + sort_as(other.begin(), other.end()); + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ + const type_info &type() const noexcept { + return *info; + } + + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *info; + deletion_policy mode; + underlying_type head; +}; + +} // 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/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" +#ifndef ENTT_ENTITY_COMPONENT_HPP +#define ENTT_ENTITY_COMPONENT_HPP + +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct in_place_delete: std::bool_constant && std::is_move_assignable_v)> {}; + +template<> +struct in_place_delete: std::false_type {}; + +template +struct in_place_delete> + : std::true_type {}; + +template +struct page_size: std::integral_constant * ENTT_PACKED_PAGE> {}; + +template<> +struct page_size: std::integral_constant {}; + +template +struct page_size> + : std::integral_constant {}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Common way to access various properties of components. + * @tparam Type Type of component. + */ +template +struct component_traits { + static_assert(std::is_same_v, Type>, "Unsupported type"); + + /*! @brief Component type. */ + using type = Type; + + /*! @brief Pointer stability, default is `false`. */ + static constexpr bool in_place_delete = internal::in_place_delete::value; + /*! @brief Page size, default is `ENTT_PACKED_PAGE` for non-empty types. */ + static constexpr std::size_t page_size = internal::page_size::value; +}; + +} // namespace entt + +#endif + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + return (*payload)[pos / Size][fast_mod(pos, Size)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + const auto pos = index(); + return (*payload)[pos / Size] + fast_mod(pos, Size); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + 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 iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Type of objects assigned to the entities. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::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), get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + } + +private: + const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); + + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); + + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } + + using std::swap; + swap(elem, element_at(to)); + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, 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. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @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 Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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{traits_type::in_place_delete}, allocator}, + payload{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + + shrink_to_size(0u); + base_type::operator=(std::move(other)); + payload = std::move(other.payload); + 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; + base_type::swap(other); + swap(payload, other.payload); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_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. + * @return Iterator pointing to the last element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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{traits_type::in_place_delete}, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element"); + return underlying_type::traits_type::combine(static_cast(pos), {}); + } + +protected: + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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::swap_only, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(underlying_type::traits_type::to_entity(hint)); + const auto entt = *base_type::try_emplace(hint, true); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size() - 1u), false); + } + + return entt; + } else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) { + return emplace(); + } else { + return *base_type::try_emplace(hint, true); + } + } + + /** + * @brief Updates a given identifier. + * @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::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable 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 insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(base_type::free_list()), true); + } + } + + /** + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. + */ + template + [[deprecated("use sort_as instead")]] size_type pack(It first, It last) { + base_type::sort_as(first, last); + return static_cast(std::distance(first, last)); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept { + return base_type::free_list(); + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + [[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept { + base_type::free_list(len); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}}; + } +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, const std::tuple &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; + } +}; + +template +class group_handler; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + template + void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence) { + (std::get(pools)->swap_elements(std::get(pools)->data()[pos], entt), ...); + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt, std::index_sequence_for{}); + } + } + +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } + } + + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; +}; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + +public: + using common_type = base_type; + + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); + } + } + + common_type &handle() noexcept { + return elem; + } + + const common_type &handle() const noexcept { + return elem; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + base_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + + /** + * @brief Sort entities according to their order in a range. + * @param other The storage to use to impose the order. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const { + sort_as(other.begin(), other.end()); + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are either owned types or not but still such that they are + * iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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 { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "entity/handle.hpp" +#ifndef ENTT_ENTITY_HANDLE_HPP +#define ENTT_ENTITY_HANDLE_HPP + +#include +#include +#include +#include +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class handle_storage_iterator final { + template + friend class handle_storage_iterator; + + using underlying_type = std::remove_reference_t; + using entity_type = typename underlying_type::entity_type; + +public: + using value_type = typename std::iterator_traits::value_type; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr handle_storage_iterator() noexcept + : entt{null}, + it{}, + last{} {} + + constexpr handle_storage_iterator(entity_type value, It from, It to) noexcept + : entt{value}, + it{from}, + last{to} { + while(it != last && !it->second.contains(entt)) { + ++it; + } + } + + constexpr handle_storage_iterator &operator++() noexcept { + while(++it != last && !it->second.contains(entt)) {} + return *this; + } + + constexpr handle_storage_iterator operator++(int) noexcept { + handle_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *it; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr bool operator==(const handle_storage_iterator &, const handle_storage_iterator &) noexcept; + +private: + entity_type entt; + It it; + It last; +}; + +template +[[nodiscard]] constexpr bool operator==(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const handle_storage_iterator &lhs, const handle_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Non-owning handle to an entity. + * + * Tiny wrapper around a registry and an entity. + * + * @tparam Registry Basic registry type. + * @tparam Scope Types to which to restrict the scope of a handle. + */ +template +struct basic_handle { + /*! @brief Type of registry accepted by the handle. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Underlying version type. */ + using version_type = typename registry_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = typename registry_type::size_type; + + /*! @brief Constructs an invalid handle. */ + basic_handle() noexcept + : reg{}, + entt{null} {} + + /** + * @brief Constructs a handle from a given registry and entity. + * @param ref An instance of the registry class. + * @param value A valid identifier. + */ + basic_handle(registry_type &ref, entity_type value) noexcept + : reg{&ref}, + entt{value} {} + + /** + * @brief Returns an iterable object to use to _visit_ a handle. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage.
+ * Returned storage are those that contain the entity associated with the + * handle. + * + * @return An iterable object to use to _visit_ the handle. + */ + [[nodiscard]] auto storage() const noexcept { + auto iterable = reg->storage(); + using iterator_type = internal::handle_storage_iterator; + return iterable_adaptor{iterator_type{entt, iterable.begin(), iterable.end()}, iterator_type{entt, iterable.end(), iterable.end()}}; + } + + /** + * @brief Constructs a const handle from a non-const one. + * @tparam Other A valid entity type. + * @tparam Args Scope of the handle to construct. + * @return A const handle referring to the same registry and the same + * entity. + */ + template + operator basic_handle() const noexcept { + static_assert(std::is_same_v || std::is_same_v, Registry>, "Invalid conversion between different handles"); + static_assert((sizeof...(Scope) == 0 || ((sizeof...(Args) != 0 && sizeof...(Args) <= sizeof...(Scope)) && ... && (type_list_contains_v, Args>))), "Invalid conversion between different handles"); + + return reg ? basic_handle{*reg, entt} : basic_handle{}; + } + + /** + * @brief Converts a handle to its underlying entity. + * @return The contained identifier. + */ + [[nodiscard]] operator entity_type() const noexcept { + return entity(); + } + + /** + * @brief Checks if a handle refers to non-null registry pointer and entity. + * @return True if the handle refers to non-null registry and entity, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return reg && reg->valid(entt); + } + + /** + * @brief Checks if a handle refers to a valid entity or not. + * @return True if the handle refers to a valid entity, false otherwise. + */ + [[nodiscard]] bool valid() const { + return reg->valid(entt); + } + + /** + * @brief Returns a pointer to the underlying registry, if any. + * @return A pointer to the underlying registry, if any. + */ + [[nodiscard]] registry_type *registry() const noexcept { + return reg; + } + + /** + * @brief Returns the entity associated with a handle. + * @return The entity associated with the handle. + */ + [[nodiscard]] entity_type entity() const noexcept { + return entt; + } + + /*! @brief Destroys the entity associated with a handle. */ + void destroy() { + reg->destroy(std::exchange(entt, null)); + } + + /** + * @brief Destroys the entity associated with a handle. + * @param version A desired version upon destruction. + */ + void destroy(const version_type version) { + reg->destroy(std::exchange(entt, null), version); + } + + /** + * @brief Assigns the given component to a handle. + * @tparam Component Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns or replaces the given component for a handle. + * @tparam Component Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template emplace_or_replace(entt, std::forward(args)...); + } + + /** + * @brief Patches the given component for a handle. + * @tparam Component Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(Func &&...func) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for a handle. + * @tparam Component Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template replace(entt, std::forward(args)...); + } + + /** + * @brief Removes the given components from a handle. + * @tparam Component Types of components to remove. + * @return The number of components actually removed. + */ + template + size_type remove() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template remove(entt); + } + + /** + * @brief Erases the given components from a handle. + * @tparam Component Types of components to erase. + */ + template + void erase() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + reg->template erase(entt); + } + + /** + * @brief Checks if a handle has all the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has all the components, false otherwise. + */ + template + [[nodiscard]] decltype(auto) all_of() const { + return reg->template all_of(entt); + } + + /** + * @brief Checks if a handle has at least one of the given components. + * @tparam Component Components for which to perform the check. + * @return True if the handle has at least one of the given components, + * false otherwise. + */ + template + [[nodiscard]] decltype(auto) any_of() const { + return reg->template any_of(entt); + } + + /** + * @brief Returns references to the given components for a handle. + * @tparam Component Types of components to get. + * @return References to the components owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template get(entt); + } + + /** + * @brief Returns a reference to the given component for a handle. + * @tparam Component Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the handle. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(Args &&...args) const { + static_assert(((sizeof...(Scope) == 0) || ... || std::is_same_v), "Invalid type"); + return reg->template get_or_emplace(entt, std::forward(args)...); + } + + /** + * @brief Returns pointers to the given components for a handle. + * @tparam Component Types of components to get. + * @return Pointers to the components owned by the handle. + */ + template + [[nodiscard]] auto try_get() const { + static_assert(sizeof...(Scope) == 0 || (type_list_contains_v, Component> && ...), "Invalid type"); + return reg->template try_get(entt); + } + + /** + * @brief Checks if a handle has components assigned. + * @return True if the handle has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan() const { + return reg->orphan(entt); + } + +private: + registry_type *reg; + entity_type entt; +}; + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same registry and the same + * entity, false otherwise. + */ +template +[[nodiscard]] bool operator==(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return lhs.registry() == rhs.registry() && lhs.entity() == rhs.entity(); +} + +/** + * @brief Compares two handles. + * @tparam Args Scope of the first handle. + * @tparam Other Scope of the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return False if both handles refer to the same registry and the same + * entity, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const basic_handle &lhs, const basic_handle &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace entt + +#endif + +// #include "entity/helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #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" +#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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "group.hpp" +#ifndef ENTT_ENTITY_GROUP_HPP +#define ENTT_ENTITY_GROUP_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class extended_group_iterator; + +template +class extended_group_iterator, get_t> { + template + auto index_to_element([[maybe_unused]] Type &cpool) const { + if constexpr(Type::traits_type::page_size == 0u) { + return std::make_tuple(); + } else { + return std::forward_as_tuple(cpool.rbegin()[it.index()]); + } + } + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + using value_type = decltype(std::tuple_cat(std::make_tuple(*std::declval()), std::declval().get_as_tuple({})..., std::declval().get_as_tuple({})...)); + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_group_iterator() + : it{}, + pools{} {} + + extended_group_iterator(iterator_type from, const std::tuple &cpools) + : it{from}, + pools{cpools} {} + + extended_group_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_group_iterator operator++(int) noexcept { + extended_group_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::tuple_cat(std::make_tuple(*it), index_to_element(*std::get(pools))..., std::get(pools)->get_as_tuple(*it)...); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend constexpr bool operator==(const extended_group_iterator &, const extended_group_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_group_iterator &lhs, const extended_group_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +struct group_descriptor { + using size_type = std::size_t; + virtual ~group_descriptor() = default; + virtual size_type owned(const id_type *, const size_type) const noexcept { + return 0u; + } +}; + +template +class group_handler; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v...>, "Groups do not support in-place delete"); + static_assert(!std::disjunction_v..., std::is_const..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + template + void swap_elements(const std::size_t pos, const entity_type entt, std::index_sequence) { + (std::get(pools)->swap_elements(std::get(pools)->data()[pos], entt), ...); + } + + void push_on_construct(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void push_on_destroy(const entity_type entt) { + if(std::apply([entt, len = len](auto *cpool, auto *...other) { return cpool->contains(entt) && !(cpool->index(entt) < len) && (other->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + swap_elements(len++, entt, std::index_sequence_for{}); + } + } + + void remove_if(const entity_type entt) { + if(std::get<0>(pools)->contains(entt) && (std::get<0>(pools)->index(entt) < len)) { + swap_elements(--len, entt, std::index_sequence_for{}); + } + } + +public: + using size_type = typename base_type::size_type; + + group_handler(Owned &...opool, Get &...gpool, Exclude &...epool) + : pools{&opool..., &gpool...}, + filter{&epool...}, + len{} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + // we cannot iterate backwards because we want to leave behind valid entities in case of owned types + for(auto *first = std::get<0>(pools)->data(), *last = first + std::get<0>(pools)->size(); first != last; ++first) { + push_on_construct(*first); + } + } + + size_type owned(const id_type *elem, const size_type length) const noexcept final { + size_type cnt = 0u; + + for(auto pos = 0u; pos < length; ++pos) { + cnt += ((elem[pos] == entt::type_hash::value()) || ...); + } + + return cnt; + } + + [[nodiscard]] size_type length() const noexcept { + return len; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + std::size_t len; +}; + +template +class group_handler, get_t, exclude_t> final: public group_descriptor { + // nasty workaround for an issue with the toolset v141 that doesn't accept a fold expression here + static_assert(!std::disjunction_v..., std::is_const...>, "Const storage type not allowed"); + + using base_type = std::common_type_t; + using entity_type = typename base_type::entity_type; + + void push_on_construct(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (!cpool->contains(entt) && ...); }, filter)) { + elem.push(entt); + } + } + + void push_on_destroy(const entity_type entt) { + if(!elem.contains(entt) + && std::apply([entt](auto *...cpool) { return (cpool->contains(entt) && ...); }, pools) + && std::apply([entt](auto *...cpool) { return (0u + ... + cpool->contains(entt)) == 1u; }, filter)) { + elem.push(entt); + } + } + + void remove_if(const entity_type entt) { + elem.remove(entt); + } + +public: + using common_type = base_type; + + template + group_handler(const Alloc &alloc, Get &...gpool, Exclude &...epool) + : pools{&gpool...}, + filter{&epool...}, + elem{alloc} { + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::push_on_construct>(*this), cpool->on_destroy().template connect<&group_handler::remove_if>(*this)), ...); }, pools); + std::apply([this](auto *...cpool) { ((cpool->on_construct().template connect<&group_handler::remove_if>(*this), cpool->on_destroy().template connect<&group_handler::push_on_destroy>(*this)), ...); }, filter); + + for(const auto entity: static_cast(*std::get<0>(pools))) { + push_on_construct(entity); + } + } + + common_type &handle() noexcept { + return elem; + } + + const common_type &handle() const noexcept { + return elem; + } + + auto pools_as_tuple() const noexcept { + return pools; + } + + auto filter_as_tuple() const noexcept { + return filter; + } + +private: + std::tuple pools; + std::tuple filter; + base_type elem; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Group. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +class basic_group; + +/** + * @brief Non-owning group. + * + * A non-owning group returns all entities and only the entities that are at + * least in the given storage. Moreover, it's guaranteed that the entity list is + * tightly packed in memory for fast iterations. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return descriptor->handle(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that are part of the group. + * @return Number of entities that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? handle().size() : size_type{}; + } + + /** + * @brief Returns the number of elements that a group has currently + * allocated space for. + * @return Capacity of the group. + */ + [[nodiscard]] size_type capacity() const noexcept { + return *this ? handle().capacity() : size_type{}; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() { + if(*this) { + descriptor->handle().shrink_to_fit(); + } + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || handle().empty(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? handle().begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? handle().rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return *this ? handle().find(entt) : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entt: *this) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), get(entt))); + } else { + std::apply(func, get(entt)); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return iterable{{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &..., const Type &...); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are such that they are iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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) { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + descriptor->handle().sort(std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, cpools = pools()](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + descriptor->handle().sort(std::move(comp), std::move(algo), std::forward(args)...); + } + } + } + + /** + * @brief Sort entities according to their order in a range. + * + * The shared pool of entities and thus its order is affected by the changes + * to each and every pool that it tracks. + * + * @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 sort_as(It first, It last) const { + if(*this) { + descriptor->handle().sort_as(first, last); + } + } + + /** + * @brief Sort entities according to their order in a range. + * @param other The storage to use to impose the order. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const common_type &other) const { + sort_as(other.begin(), other.end()); + } + +private: + handler *descriptor; +}; + +/** + * @brief Owning group. + * + * Owning groups returns all entities and only the entities that are at + * least in the given storage. Moreover: + * + * * It's guaranteed that the entity list is tightly packed in memory for fast + * iterations. + * * It's guaranteed that all components in the owned storage are tightly packed + * in memory for even faster iterations and to allow direct access. + * * They stay true to the order of the owned storage and all instances have the + * same order in memory. + * + * The more types of storage are owned, the faster it is to iterate a group. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the pools iterated by the group in any way + * invalidates all the iterators. + * + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + */ +template +class basic_group, get_t, exclude_t> { + using base_type = std::common_type_t; + using underlying_type = typename base_type::entity_type; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + auto pools() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->pools_as_tuple() : return_type{}; + } + + auto filter() const noexcept { + using return_type = std::tuple; + return descriptor ? descriptor->filter_as_tuple() : return_type{}; + } + +public: + /*! @brief Underlying entity identifier. */ + using entity_type = underlying_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + /*! @brief Iterable group type. */ + using iterable = iterable_adaptor, get_t>>; + /*! @brief Group handler type. */ + using handler = internal::group_handler...>, get_t...>, exclude_t...>>; + + /*! @brief Default constructor to use to create empty, invalid groups. */ + basic_group() noexcept + : descriptor{} {} + + /** + * @brief Constructs a group from a set of storage classes. + * @param ref A reference to a group handler. + */ + basic_group(handler &ref) noexcept + : descriptor{&ref} {} + + /** + * @brief Returns the leading storage of a group. + * @return The leading storage of the group. + */ + [[nodiscard]] const common_type &handle() const noexcept { + return *storage<0>(); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + constexpr auto offset = sizeof...(Owned) + sizeof...(Get); + + if constexpr(Index < offset) { + return std::get(pools()); + } else { + return std::get(filter()); + } + } + + /** + * @brief Returns the number of entities that that are part of the group. + * @return Number of entities that that are part of the group. + */ + [[nodiscard]] size_type size() const noexcept { + return *this ? descriptor->length() : size_type{}; + } + + /** + * @brief Checks whether a group is empty. + * @return True if the group is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !*this || !descriptor->length(); + } + + /** + * @brief Returns an iterator to the first entity of the group. + * + * If the group is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the group. + */ + [[nodiscard]] iterator begin() const noexcept { + return *this ? (handle().end() - descriptor->length()) : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the group. + * @return An iterator to the entity following the last entity of the + * group. + */ + [[nodiscard]] iterator end() const noexcept { + return *this ? handle().end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed group. + * + * If the group is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed group. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return *this ? handle().rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * group. + * @return An iterator to the entity following the last entity of the + * reversed group. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return *this ? (handle().rbegin() + descriptor->length()) : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the group, if any. + * @return The first entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the group, if any. + * @return The last entity of the group if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + const auto it = rbegin(); + return it != rend() ? *it : null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + const auto it = *this ? handle().find(entt) : iterator{}; + return it >= begin() ? it : iterator{}; + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const { + return begin()[pos]; + } + + /** + * @brief Checks if a group is properly initialized. + * @return True if the group is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return descriptor != nullptr; + } + + /** + * @brief Checks if a group contains an entity. + * @param entt A valid identifier. + * @return True if the group contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return *this && handle().contains(entt) && (handle().index(entt) < (descriptor->length())); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, cpools); + } else if constexpr(sizeof...(Index) == 1) { + return (std::get(cpools)->get(entt), ...); + } else { + return std::tuple_cat(std::get(cpools)->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The function object is invoked for each entity. It is provided with the + * entity itself and a set of references to non-empty components. The + * _constness_ of the components is as requested.
+ * The signature of the function must be equivalent to one of the following + * forms: + * + * @code{.cpp} + * void(const entity_type, Type &...); + * void(Type &...); + * @endcode + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(auto args: each()) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, args); + } else { + std::apply([&func](auto, auto &&...less) { func(std::forward(less)...); }, args); + } + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a group. + * + * The iterable object returns tuples that contain the current entity and a + * set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @note + * Empty types aren't explicitly instantiated and therefore they are never + * returned during iterations. + * + * @return An iterable object to use to _visit_ the group. + */ + [[nodiscard]] iterable each() const noexcept { + const auto cpools = pools(); + return {{begin(), cpools}, {end(), cpools}}; + } + + /** + * @brief Sort a group according to the given comparison function. + * + * The comparison function object must return `true` if the first element + * is _less_ than the second one, `false` otherwise. The signature of the + * comparison function should be equivalent to one of the following: + * + * @code{.cpp} + * bool(std::tuple, std::tuple); + * bool(const Type &, const Type &); + * bool(const Entity, const Entity); + * @endcode + * + * Where `Type` are either owned types or not but still such that they are + * iterated by the group.
+ * Moreover, the comparison function object shall induce a + * _strict weak ordering_ on the values. + * + * The sort function object must offer a member function template + * `operator()` that accepts three arguments: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function to use to compare the elements. + * + * @tparam Type Optional type of component to compare. + * @tparam Other Other 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 { + sort, index_of...>(std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort a group according to the given comparison function. + * + * @sa sort + * + * @tparam Index Optional indexes 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 { + const auto cpools = pools(); + + if constexpr(sizeof...(Index) == 0) { + static_assert(std::is_invocable_v, "Invalid comparison function"); + storage<0>()->sort_n(descriptor->length(), std::move(compare), std::move(algo), std::forward(args)...); + } else { + auto comp = [&compare, &cpools](const entity_type lhs, const entity_type rhs) { + if constexpr(sizeof...(Index) == 1) { + return compare((std::get(cpools)->get(lhs), ...), (std::get(cpools)->get(rhs), ...)); + } else { + return compare(std::forward_as_tuple(std::get(cpools)->get(lhs)...), std::forward_as_tuple(std::get(cpools)->get(rhs)...)); + } + }; + + storage<0>()->sort_n(descriptor->length(), std::move(comp), std::move(algo), std::forward(args)...); + } + + auto cb = [this](auto *head, auto *...other) { + for(auto next = descriptor->length(); next; --next) { + const auto pos = next - 1; + [[maybe_unused]] const auto entt = head->data()[pos]; + (other->swap_elements(other->data()[pos], entt), ...); + } + }; + + std::apply(cb, cpools); + } + +private: + handler *descriptor; +}; + +} // namespace entt + +#endif + +// #include "storage.hpp" + +// #include "view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] bool all_of_but(const std::size_t index, const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != index) && it[pos]->contains(entt); ++pos) {} + + if(pos == index) { + for(++pos; (pos != len) && it[pos]->contains(entt); ++pos) {} + } + + return pos == len; +} + +template +[[nodiscard]] bool none_of(const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != len) && !(it[pos] && it[pos]->contains(entt)); ++pos) {} + return pos == len; +} + +template +[[nodiscard]] bool fully_initialized(const Type *const *it, const std::size_t len) noexcept { + std::size_t pos{}; + for(; (pos != len) && it[pos]; ++pos) {} + return pos == len; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + elem.filter = {view.template storage()..., other.template storage()...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid(const typename iterator_type::value_type entt) const noexcept { + return ((Get != 1u) || (entt != tombstone)) && all_of_but(index, pools.data(), Get, entt) && none_of(filter.data(), Exclude, entt); + } + +public: + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using difference_type = typename iterator_type::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl, const std::size_t idx) noexcept + : it{curr}, + last{to}, + pools{value}, + filter{excl}, + index{idx} { + while(it != last && !valid(*it)) { + ++it; + } + } + + view_iterator &operator++() noexcept { + while(++it != last && !valid(*it)) {} + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; + std::size_t index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +struct extended_view_iterator final { + using iterator_type = It; + 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; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{}, + pools{} {} + + extended_view_iterator(iterator_type from, std::tuple value) + : it{from}, + pools{value} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, components are + * added or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept = default; + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + leading{}, + index{Get} { + unchecked_refresh(); + } + + void use(const std::size_t pos) noexcept { + if(leading) { + index = pos; + leading = pools[index]; + } + } + + void unchecked_refresh() noexcept { + index = 0u; + + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + + leading = pools[index]; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = (leading != nullptr) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? iterator{leading->begin(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? iterator{leading->end(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(leading) { + auto it = leading->rbegin(0); + const auto last = leading->rend(0); + for(; it != last && !contains(*it); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{leading->find(entt), leading->end(), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return leading && internal::fully_initialized(filter.data(), Exclude); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if(leading) { + const auto idx = leading->find(entt).index(); + return (!(idx < 0 || idx > leading->begin(0).index())) && internal::all_of_but(index, pools.data(), Get, entt) && internal::none_of(filter.data(), Exclude, entt); + } + + return false; + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + std::array pools{}; + std::array filter{}; + const common_type *leading{}; + size_type index{Get}; + /*! @endcond */ +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t>: public basic_common_view, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, sizeof...(Get), sizeof...(Exclude)>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + auto storage(std::index_sequence) const noexcept { + return std::make_tuple(storage()...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of_but(this->index, this->pools.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((storage() == base_type::handle() ? each(func, seq) : void()), ...); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Bidirectional iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + + if constexpr(Index < sizeof...(Get)) { + return static_cast(const_cast *>(this->pools[Index])); + } else { + return static_cast(const_cast *>(this->filter[Index - sizeof...(Get)])); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v> &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + this->pools[Index] = &elem; + base_type::refresh(); + } else { + this->filter[Index - sizeof...(Get)] = &elem; + } + } + + /** + * @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 Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for{})); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @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 { + if(base_type::handle() != nullptr) { + pick_and_each(func, std::index_sequence_for{}); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + const auto as_pools = storage(std::index_sequence_for{}); + return {internal::extended_view_iterator{base_type::begin(), as_pools}, internal::extended_view_iterator{base_type::end(), as_pools}}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + */ +template +class basic_storage_view { +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} {} + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + [[nodiscard]] size_type size() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !leading || leading->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? leading->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? leading->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return leading ? leading->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + return empty() ? null : *leading->begin(); + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + return empty() ? null : *leading->rbegin(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return leading ? leading->find(entt) : iterator{}; + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return leading && leading->contains(entt); + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + const common_type *leading{}; + /*! @endcond */ +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>>: public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = decltype(std::declval().each()); + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(this->leading)); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + this->leading = &elem; + } + + /** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type operator[](const size_type pos) const { + return base_type::begin()[pos]; + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Elem Type 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 { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Index Index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(auto *elem = storage(); elem) { + if constexpr(is_applicable_veach().begin())>) { + for(const auto pack: elem->each()) { + std::apply(func, pack); + } + } else if constexpr(std::is_invocable_vbegin())>) { + for(auto &&component: *elem) { + func(component); + } + } else { + for(size_type pos = elem->size(); pos; --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 noexcept { + auto *elem = storage(); + return elem ? elem->each() : iterable{}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + auto dispatch(get_t, exclude_t) const { + return reg.template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg.template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the components are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given component. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +auto to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) -> typename basic_storage::entity_type { + constexpr auto page_size = basic_storage::traits_type::page_size; + const typename basic_storage::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } + } + + return null; +} + +/** + * @copybrief to_entity + * @tparam Args Registry type template parameters. + * @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 +[[deprecated("use storage based to_entity instead")]] typename basic_registry::entity_type to_entity(const basic_registry ®, const Component &instance) { + if(const auto *storage = reg.template storage(); storage) { + return to_entity(*storage, instance); + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + +// #include "entity/mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const noexcept { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + 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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the 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 "entity.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 Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + owner_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) { + if constexpr(std::is_same_v) { + destruction.publish(reg, *it); + } else { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = *it; entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, *it); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @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) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif + +// #include "entity/observer.hpp" +#ifndef ENTT_ENTITY_OBSERVER_HPP +#define ENTT_ENTITY_OBSERVER_HPP + +#include +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "storage.hpp" + + +namespace entt { + +/*! @brief Grouping matcher. */ +template +struct matcher {}; + +/** + * @brief Collector. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error, but for a few reasonable cases. + */ +template +struct basic_collector; + +/** + * @brief Collector. + * + * A collector contains a set of rules (literally, matchers) to use to track + * entities.
+ * Its main purpose is to generate a descriptor that allows an observer to know + * how to connect to a registry. + */ +template<> +struct basic_collector<> { + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = exclude_t{}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() noexcept { + return basic_collector, type_list<>, AnyOf>>{}; + } +}; + +/** + * @brief Collector. + * @copydetails basic_collector<> + * @tparam Reject Untracked types used to filter out entities. + * @tparam Require Untracked types required by the matcher. + * @tparam Rule Specific details of the current matcher. + * @tparam Other Other matchers. + */ +template +struct basic_collector, type_list, Rule...>, Other...> { + /*! @brief Current matcher. */ + using current_type = matcher, type_list, Rule...>; + + /** + * @brief Adds a grouping matcher to the collector. + * @tparam AllOf Types of components tracked by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto group(exclude_t = exclude_t{}) noexcept { + return basic_collector, type_list<>, type_list, AllOf...>, current_type, Other...>{}; + } + + /** + * @brief Adds an observing matcher to the collector. + * @tparam AnyOf Type of component for which changes should be detected. + * @return The updated collector. + */ + template + static constexpr auto update() noexcept { + return basic_collector, type_list<>, AnyOf>, current_type, Other...>{}; + } + + /** + * @brief Updates the filter of the last added matcher. + * @tparam AllOf Types of components required by the matcher. + * @tparam NoneOf Types of components used to filter out entities. + * @return The updated collector. + */ + template + static constexpr auto where(exclude_t = exclude_t{}) noexcept { + using extended_type = matcher, type_list, Rule...>; + return basic_collector{}; + } +}; + +/*! @brief Variable template used to ease the definition of collectors. */ +inline constexpr basic_collector<> collector{}; + +/** + * @brief Observer. + * + * An observer returns all the entities and only the entities that fit the + * requirements of at least one matcher. Moreover, it's guaranteed that the + * entity list is tightly packed in memory for fast iterations.
+ * In general, observers don't stay true to the order of any set of components. + * + * Observers work mainly with two types of matchers, provided through a + * collector: + * + * * Observing matcher: an observer will return at least all the living entities + * for which one or more of the given components have been updated and not yet + * destroyed. + * * Grouping matcher: an observer will return at least all the living entities + * that would have entered the given group if it existed and that would have + * not yet left it. + * + * If an entity respects the requirements of multiple matchers, it will be + * returned once and only once by the observer in any case. + * + * Matchers support also filtering by means of a _where_ clause that accepts + * both a list of types and an exclusion list.
+ * Whenever a matcher finds that an entity matches its requirements, the + * condition of the filter is verified before to register the entity itself. + * Moreover, a registered entity isn't returned by the observer if the condition + * set by the filter is broken in the meantime. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New instances of the given components are created and assigned to entities. + * * The entity currently pointed is modified (as an example, if one of the + * given components is removed from the entity to which the iterator points). + * * The entity currently pointed is destroyed. + * + * In all the other cases, modifying the pools of the given components in any + * way invalidates all the iterators. + * + * @warning + * Lifetime of an observer doesn't necessarily have to overcome that of the + * registry to which it is connected. However, the observer must be disconnected + * from the registry before being destroyed to avoid crashes due to dangling + * pointers. + * + * @tparam Registry Basic registry type. + * @tparam Mask Mask type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_observer: private basic_storage { + using base_type = basic_storage; + + template + struct matcher_handler; + + template + struct matcher_handler, type_list, AnyOf>> { + template + static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { + if(reg.template all_of(entt) && !reg.template any_of(entt)) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + reg.template on_update().template connect<&maybe_valid_if>(obs); + reg.template on_destroy().template connect<&discard_if>(obs); + } + + static void disconnect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + reg.template on_update().disconnect(&obs); + reg.template on_destroy().disconnect(&obs); + } + }; + + template + struct matcher_handler, type_list, type_list, AllOf...>> { + template + static void maybe_valid_if(basic_observer &obs, Registry ®, const typename Registry::entity_type entt) { + auto condition = [®, entt]() { + if constexpr(sizeof...(Ignore) == 0) { + return reg.template all_of(entt) && !reg.template any_of(entt); + } else { + return reg.template all_of(entt) && ((std::is_same_v || !reg.template any_of(entt)) && ...) && !reg.template any_of(entt); + } + }; + + if(condition()) { + if(!obs.contains(entt)) { + obs.emplace(entt); + } + + obs.get(entt) |= (1 << Index); + } + } + + template + static void discard_if(basic_observer &obs, Registry &, const typename Registry::entity_type entt) { + if(obs.contains(entt) && !(obs.get(entt) &= (~(1 << Index)))) { + obs.erase(entt); + } + } + + template + static void connect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&maybe_valid_if>(obs), ...); + (reg.template on_destroy().template connect<&discard_if>(obs), ...); + (reg.template on_construct().template connect<&discard_if>(obs), ...); + } + + static void disconnect(basic_observer &obs, Registry ®) { + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_destroy().disconnect(&obs), ...); + (reg.template on_construct().disconnect(&obs), ...); + } + }; + + template + static void disconnect(Registry ®, basic_observer &obs) { + (matcher_handler::disconnect(obs, reg), ...); + } + + template + void connect(Registry ®, std::index_sequence) { + static_assert(sizeof...(Matcher) < std::numeric_limits::digits, "Too many matchers"); + (matcher_handler::template connect(*this, reg), ...); + release.template connect<&basic_observer::disconnect>(reg); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = typename registry_type::common_type::iterator; + + /*! @brief Default constructor. */ + basic_observer() + : basic_observer{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_observer(const allocator_type &allocator) + : base_type{allocator}, + release{} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + basic_observer(const basic_observer &) = delete; + + /*! @brief Default move constructor, deleted on purpose. */ + basic_observer(basic_observer &&) = delete; + + /** + * @brief Creates an observer and connects it to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + * @param allocator The allocator to use. + */ + template + basic_observer(registry_type ®, basic_collector, const allocator_type &allocator = allocator_type{}) + : basic_observer{allocator} { + connect(reg, std::index_sequence_for{}); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(const basic_observer &) = delete; + + /** + * @brief Default move assignment operator, deleted on purpose. + * @return This observer. + */ + basic_observer &operator=(basic_observer &&) = delete; + + /** + * @brief Connects an observer to a given registry. + * @tparam Matcher Types of matchers to use to initialize the observer. + * @param reg A valid reference to a registry. + */ + template + void connect(registry_type ®, basic_collector) { + disconnect(); + connect(reg, std::index_sequence_for{}); + base_type::clear(); + } + + /*! @brief Disconnects an observer from the registry it keeps track of. */ + void disconnect() { + if(release) { + release(*this); + release.reset(); + } + } + + /** + * @brief Returns the number of elements in an observer. + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return base_type::size(); + } + + /** + * @brief Checks whether an observer is empty. + * @return True if the observer is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return base_type::empty(); + } + + /** + * @brief Direct access to the list of entities of the observer. + * + * The returned pointer is such that range `[data(), data() + size())` is + * always a valid range, even if the container is empty. + * + * @note + * Entities are in the reverse order as returned by the `begin`/`end` + * iterators. + * + * @return A pointer to the array of entities. + */ + [[nodiscard]] const entity_type *data() const noexcept { + return base_type::data(); + } + + /** + * @brief Returns an iterator to the first entity of the observer. + * + * If the observer is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the observer. + */ + [[nodiscard]] iterator begin() const noexcept { + return base_type::base_type::begin(); + } + + /** + * @brief Returns an iterator that is past the last entity of the observer. + * @return An iterator to the entity following the last entity of the + * observer. + */ + [[nodiscard]] iterator end() const noexcept { + return base_type::base_type::end(); + } + + /*! @brief Clears the underlying container. */ + void clear() noexcept { + base_type::clear(); + } + + /** + * @brief Iterates entities and applies the given function object to them. + * + * The function object is invoked for each entity.
+ * The signature of the function must be equivalent to the following form: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + + /** + * @brief Iterates entities and applies the given function object to them, + * then clears the observer. + * + * @sa each + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) { + std::as_const(*this).each(std::move(func)); + clear(); + } + +private: + delegate release; +}; + +} // namespace entt + +#endif + +// #include "entity/organizer.hpp" +#ifndef ENTT_ENTITY_ORGANIZER_HPP +#define ENTT_ENTITY_ORGANIZER_HPP + +#include +#include +#include +#include +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "../graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "../graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "helper.hpp" +#ifndef ENTT_ENTITY_HELPER_HPP +#define ENTT_ENTITY_HELPER_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../signal/delegate.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/** + * @brief Converts a registry to a view. + * @tparam Registry Basic registry type. + */ +template +class as_view { + template + auto dispatch(get_t, exclude_t) const { + return reg.template view...>(exclude_t...>{}); + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_view(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a view. + * @tparam Get Type of storage used to construct the view. + * @tparam Exclude Types of storage used to filter the view. + * @return A newly created view. + */ + template + operator basic_view() const { + return dispatch(Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Converts a registry to a group. + * @tparam Registry Basic registry type. + */ +template +class as_group { + template + auto dispatch(owned_t, get_t, exclude_t) const { + if constexpr(std::is_const_v) { + return reg.template group_if_exists(get_t{}, exclude_t{}); + } else { + return reg.template group...>(get_t...>{}, exclude_t...>{}); + } + } + +public: + /*! @brief Type of registry to convert. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs a converter for a given registry. + * @param source A valid reference to a registry. + */ + as_group(registry_type &source) noexcept + : reg{source} {} + + /** + * @brief Conversion function from a registry to a group. + * @tparam Owned Types of _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group. + * @tparam Exclude Types of storage used to filter the group. + * @return A newly created group. + */ + template + operator basic_group() const { + return dispatch(Owned{}, Get{}, Exclude{}); + } + +private: + registry_type ® +}; + +/** + * @brief Helper to create a listener that directly invokes a member function. + * @tparam Member Member function to invoke on a component of the given type. + * @tparam Registry Basic registry type. + * @param reg A registry that contains the given entity and its components. + * @param entt Entity from which to get the component. + */ +template>> +void invoke(Registry ®, const typename Registry::entity_type entt) { + static_assert(std::is_member_function_pointer_v, "Invalid pointer to non-static member function"); + delegate func; + func.template connect(reg.template get>(entt)); + func(reg, entt); +} + +/** + * @brief Returns the entity associated with a given component. + * + * @warning + * Currently, this function only works correctly with the default storage as it + * makes assumptions about how the components are laid out. + * + * @tparam Args Storage type template parameters. + * @param storage A storage that contains the given component. + * @param instance A valid component instance. + * @return The entity associated with the given component. + */ +template +auto to_entity(const basic_storage &storage, const typename basic_storage::value_type &instance) -> typename basic_storage::entity_type { + constexpr auto page_size = basic_storage::traits_type::page_size; + const typename basic_storage::base_type &base = storage; + const auto *addr = std::addressof(instance); + + for(auto it = base.rbegin(), last = base.rend(); it < last; it += page_size) { + if(const auto dist = (addr - std::addressof(storage.get(*it))); dist >= 0 && dist < static_cast(page_size)) { + return *(it + dist); + } + } + + return null; +} + +/** + * @copybrief to_entity + * @tparam Args Registry type template parameters. + * @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 +[[deprecated("use storage based to_entity instead")]] typename basic_registry::entity_type to_entity(const basic_registry ®, const Component &instance) { + if(const auto *storage = reg.template storage(); storage) { + return to_entity(*storage, instance); + } + + return null; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct sigh_helper; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + */ +template +struct sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + */ + sigh_helper(registry_type &ref) + : bucket{&ref} {} + + /** + * @brief Binds a properly initialized helper to a given signal type. + * @tparam Type Type of signal to bind the helper to. + * @param id Optional name for the underlying storage to use. + * @return A helper for a given registry and signal type. + */ + template + auto with(const id_type id = type_hash::value()) noexcept { + return sigh_helper{*bucket, id}; + } + + /** + * @brief Returns a reference to the underlying registry. + * @return A reference to the underlying registry. + */ + [[nodiscard]] registry_type ®istry() noexcept { + return *bucket; + } + +private: + registry_type *bucket; +}; + +/** + * @brief Signal connection helper for registries. + * @tparam Registry Basic registry type. + * @tparam Type Type of signal to connect listeners to. + */ +template +struct sigh_helper final: sigh_helper { + /*! @brief Registry type. */ + using registry_type = Registry; + + /** + * @brief Constructs a helper for a given registry. + * @param ref A valid reference to a registry. + * @param id Optional name for the underlying storage to use. + */ + sigh_helper(registry_type &ref, const id_type id = type_hash::value()) + : sigh_helper{ref}, + name{id} {} + + /** + * @brief Forwards the call to `on_construct` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_construct(Args &&...args) { + this->registry().template on_construct(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_update` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_update(Args &&...args) { + this->registry().template on_update(name).template connect(std::forward(args)...); + return *this; + } + + /** + * @brief Forwards the call to `on_destroy` on the underlying storage. + * @tparam Candidate Function or member to connect. + * @tparam Args Type of class or type of payload, if any. + * @param args A valid object that fits the purpose, if any. + * @return This helper. + */ + template + auto on_destroy(Args &&...args) { + this->registry().template on_destroy(name).template connect(std::forward(args)...); + return *this; + } + +private: + id_type name; +}; + +/** + * @brief Deduction guide. + * @tparam Registry Basic registry type. + */ +template +sigh_helper(Registry &) -> sigh_helper; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct is_view: std::false_type {}; + +template +struct is_view>: std::true_type {}; + +template +inline constexpr bool is_view_v = is_view::value; + +template +struct unpack_type { + using ro = std::conditional_t< + type_list_contains_v || (std::is_const_v && !type_list_contains_v>), + type_list>, + type_list<>>; + + using rw = std::conditional_t< + type_list_contains_v> || (!std::is_const_v && !type_list_contains_v), + type_list, + type_list<>>; +}; + +template +struct unpack_type, type_list> { + using ro = type_list<>; + using rw = type_list<>; +}; + +template +struct unpack_type, type_list> + : unpack_type, type_list> {}; + +template +struct unpack_type, exclude_t>, type_list> { + using ro = type_list_cat_t, typename unpack_type, type_list>::ro...>; + using rw = type_list_cat_t, type_list>::rw...>; +}; + +template +struct unpack_type, exclude_t>, type_list> + : unpack_type, exclude_t>, type_list> {}; + +template +struct resource_traits; + +template +struct resource_traits, type_list> { + using args = type_list...>; + using ro = type_list_cat_t>::ro..., typename unpack_type>::ro...>; + using rw = type_list_cat_t>::rw..., typename unpack_type>::rw...>; +}; + +template +resource_traits...>, type_list> free_function_to_resource_traits(Ret (*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (*)(Type &, Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...)); + +template +resource_traits...>, type_list> constrained_function_to_resource_traits(Ret (Class::*)(Args...) const); + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class for creating a static task graph. + * + * This class offers minimal support (but sufficient in many cases) for creating + * an execution graph from functions and their requirements on resources.
+ * Note that the resulting tasks aren't executed in any case. This isn't the + * goal of the tool. Instead, they are returned to the user in the form of a + * graph that allows for safe execution. + * + * @tparam Registry Basic registry type. + */ +template +class basic_organizer final { + using callback_type = void(const void *, Registry &); + using prepare_type = void(Registry &); + using dependency_type = std::size_t(const bool, const type_info **, const std::size_t); + + struct vertex_data final { + std::size_t ro_count{}; + std::size_t rw_count{}; + const char *name{}; + const void *payload{}; + callback_type *callback{}; + dependency_type *dependency; + prepare_type *prepare{}; + const type_info *info{}; + }; + + template + [[nodiscard]] static decltype(auto) extract(Registry ®) { + if constexpr(std::is_same_v) { + return reg; + } else if constexpr(internal::is_view_v) { + return static_cast(as_view{reg}); + } else { + return reg.ctx().template emplace>(); + } + } + + template + [[nodiscard]] static auto to_args(Registry ®, type_list) { + return std::tuple(reg))...>(extract(reg)...); + } + + template + static std::size_t fill_dependencies(type_list, [[maybe_unused]] const type_info **buffer, [[maybe_unused]] const std::size_t count) { + if constexpr(sizeof...(Type) == 0u) { + return {}; + } else { + const type_info *info[sizeof...(Type)]{&type_id()...}; + const auto length = count < sizeof...(Type) ? count : sizeof...(Type); + + for(std::size_t pos{}; pos < length; ++pos) { + buffer[pos] = info[pos]; + } + + return length; + } + } + + template + void track_dependencies(std::size_t index, const bool requires_registry, type_list, type_list) { + builder.bind(static_cast(index)); + builder.set(type_hash::value(), requires_registry || (sizeof...(RO) + sizeof...(RW) == 0u)); + (builder.ro(type_hash::value()), ...); + (builder.rw(type_hash::value()), ...); + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Raw task function type. */ + using function_type = callback_type; + + /*! @brief Vertex type of a task graph defined as an adjacency list. */ + struct vertex { + /** + * @brief Constructs a vertex of the task graph. + * @param vtype True if the vertex is a top-level one, false otherwise. + * @param data The data associated with the vertex. + * @param edges The indices of the children in the adjacency list. + */ + vertex(const bool vtype, vertex_data data, std::vector edges) + : is_top_level{vtype}, + node{std::move(data)}, + reachable{std::move(edges)} {} + + /** + * @brief Fills a buffer with the type info objects for the writable + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type ro_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(false, buffer, length); + } + + /** + * @brief Fills a buffer with the type info objects for the read-only + * resources of a vertex. + * @param buffer A buffer pre-allocated by the user. + * @param length The length of the user-supplied buffer. + * @return The number of type info objects written to the buffer. + */ + size_type rw_dependency(const type_info **buffer, const std::size_t length) const noexcept { + return node.dependency(true, buffer, length); + } + + /** + * @brief Returns the number of read-only resources of a vertex. + * @return The number of read-only resources of the vertex. + */ + size_type ro_count() const noexcept { + return node.ro_count; + } + + /** + * @brief Returns the number of writable resources of a vertex. + * @return The number of writable resources of the vertex. + */ + size_type rw_count() const noexcept { + return node.rw_count; + } + + /** + * @brief Checks if a vertex is also a top-level one. + * @return True if the vertex is a top-level one, false otherwise. + */ + bool top_level() const noexcept { + return is_top_level; + } + + /** + * @brief Returns a type info object associated with a vertex. + * @return A properly initialized type info object. + */ + const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns a user defined name associated with a vertex, if any. + * @return The user defined name associated with the vertex, if any. + */ + const char *name() const noexcept { + return node.name; + } + + /** + * @brief Returns the function associated with a vertex. + * @return The function associated with the vertex. + */ + function_type *callback() const noexcept { + return node.callback; + } + + /** + * @brief Returns the payload associated with a vertex, if any. + * @return The payload associated with the vertex, if any. + */ + const void *data() const noexcept { + return node.payload; + } + + /** + * @brief Returns the list of nodes reachable from a given vertex. + * @return The list of nodes reachable from the vertex. + */ + const std::vector &children() const noexcept { + return reachable; + } + + /** + * @brief Prepares a registry and assures that all required resources + * are properly instantiated before using them. + * @param reg A valid registry. + */ + void prepare(registry_type ®) const { + node.prepare ? node.prepare(reg) : void(); + } + + private: + bool is_top_level; + vertex_data node; + std::vector reachable; + }; + + /** + * @brief Adds a free function to the task list. + * @tparam Candidate Function to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param name Optional name to associate with the task. + */ + template + void emplace(const char *name = nullptr) { + using resource_type = decltype(internal::free_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *, registry_type ®) { + std::apply(Candidate, to_args(reg, typename resource_type::args{})); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + nullptr, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds a free function with payload or a member function with an + * instance to the task list. + * @tparam Candidate Function or member to add to the task list. + * @tparam Req Additional requirements and/or override resource access mode. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + * @param name Optional name to associate with the task. + */ + template + void emplace(Type &value_or_instance, const char *name = nullptr) { + using resource_type = decltype(internal::constrained_function_to_resource_traits(Candidate)); + constexpr auto requires_registry = type_list_contains_v; + + callback_type *callback = +[](const void *payload, registry_type ®) { + Type *curr = static_cast(const_cast *>(payload)); + std::apply(Candidate, std::tuple_cat(std::forward_as_tuple(*curr), to_args(reg, typename resource_type::args{}))); + }; + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + &value_or_instance, + callback, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + +[](registry_type ®) { void(to_args(reg, typename resource_type::args{})); }, + &type_id>()}; + + track_dependencies(vertices.size(), requires_registry, typename resource_type::ro{}, typename resource_type::rw{}); + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Adds an user defined function with optional payload to the task + * list. + * @tparam Req Additional requirements and/or override resource access mode. + * @param func Function to add to the task list. + * @param payload User defined arbitrary data. + * @param name Optional name to associate with the task. + */ + template + void emplace(function_type *func, const void *payload = nullptr, const char *name = nullptr) { + using resource_type = internal::resource_traits, type_list>; + track_dependencies(vertices.size(), true, typename resource_type::ro{}, typename resource_type::rw{}); + + vertex_data vdata{ + resource_type::ro::size, + resource_type::rw::size, + name, + payload, + func, + +[](const bool rw, const type_info **buffer, const std::size_t length) { return rw ? fill_dependencies(typename resource_type::rw{}, buffer, length) : fill_dependencies(typename resource_type::ro{}, buffer, length); }, + nullptr, + &type_id()}; + + vertices.push_back(std::move(vdata)); + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency list of the task graph. + */ + std::vector graph() { + std::vector adjacency_list{}; + adjacency_list.reserve(vertices.size()); + auto adjacency_matrix = builder.graph(); + + for(auto curr: adjacency_matrix.vertices()) { + const auto iterable = adjacency_matrix.in_edges(curr); + std::vector reachable{}; + + for(auto &&edge: adjacency_matrix.out_edges(curr)) { + reachable.push_back(edge.second); + } + + adjacency_list.emplace_back(iterable.cbegin() == iterable.cend(), vertices[curr], std::move(reachable)); + } + + return adjacency_list; + } + + /*! @brief Erases all elements from a container. */ + void clear() { + builder.clear(); + vertices.clear(); + } + +private: + std::vector vertices; + flow builder; +}; + +} // namespace entt + +#endif + +// #include "entity/registry.hpp" +#ifndef ENTT_ENTITY_REGISTRY_HPP +#define ENTT_ENTITY_REGISTRY_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "group.hpp" + +// #include "mixin.hpp" +#ifndef ENTT_ENTITY_MIXIN_HPP +#define ENTT_ENTITY_MIXIN_HPP + +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../signal/sigh.hpp" + +// #include "entity.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 Underlying storage type. + * @tparam Registry Basic registry type. + */ +template +class basic_sigh_mixin final: public Type { + using underlying_type = Type; + using owner_type = Registry; + + using basic_registry_type = basic_registry; + using sigh_type = sigh; + using underlying_iterator = typename underlying_type::base_type::basic_iterator; + + static_assert(std::is_base_of_v, "Invalid registry type"); + + owner_type &owner_or_assert() const noexcept { + ENTT_ASSERT(owner != nullptr, "Invalid pointer to registry"); + return static_cast(*owner); + } + + void pop(underlying_iterator first, underlying_iterator last) final { + if(auto ® = owner_or_assert(); destruction.empty()) { + underlying_type::pop(first, last); + } else { + for(; first != last; ++first) { + const auto entt = *first; + destruction.publish(reg, entt); + const auto it = underlying_type::find(entt); + underlying_type::pop(it, it + 1u); + } + } + } + + void pop_all() final { + if(auto ® = owner_or_assert(); !destruction.empty()) { + for(auto it = underlying_type::base_type::begin(0), last = underlying_type::base_type::end(0); it != last; ++it) { + if constexpr(std::is_same_v) { + destruction.publish(reg, *it); + } else { + if constexpr(underlying_type::traits_type::in_place_delete) { + if(const auto entt = *it; entt != tombstone) { + destruction.publish(reg, entt); + } + } else { + destruction.publish(reg, *it); + } + } + } + } + + underlying_type::pop_all(); + } + + underlying_iterator try_emplace(const typename underlying_type::entity_type entt, const bool force_back, const void *value) final { + const auto it = underlying_type::try_emplace(entt, force_back, value); + + if(auto ® = owner_or_assert(); it != underlying_type::base_type::end()) { + construction.publish(reg, *it); + } + + return it; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = typename underlying_type::allocator_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename underlying_type::entity_type; + /*! @brief Expected registry type. */ + using registry_type = owner_type; + + /*! @brief Default constructor. */ + basic_sigh_mixin() + : basic_sigh_mixin{allocator_type{}} {} + + /** + * @brief Constructs an empty storage with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_sigh_mixin(const allocator_type &allocator) + : underlying_type{allocator}, + owner{}, + construction{allocator}, + destruction{allocator}, + update{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sigh_mixin(basic_sigh_mixin &&other) noexcept + : underlying_type{std::move(other)}, + owner{other.owner}, + construction{std::move(other.construction)}, + destruction{std::move(other.destruction)}, + update{std::move(other.update)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sigh_mixin(basic_sigh_mixin &&other, const allocator_type &allocator) noexcept + : underlying_type{std::move(other), allocator}, + owner{other.owner}, + construction{std::move(other.construction), allocator}, + destruction{std::move(other.destruction), allocator}, + update{std::move(other.update), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_sigh_mixin &operator=(basic_sigh_mixin &&other) noexcept { + underlying_type::operator=(std::move(other)); + owner = other.owner; + construction = std::move(other.construction); + destruction = std::move(other.destruction); + update = std::move(other.update); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given storage. + * @param other Storage to exchange the content with. + */ + void swap(basic_sigh_mixin &other) { + using std::swap; + underlying_type::swap(other); + swap(owner, other.owner); + swap(construction, other.construction); + swap(destruction, other.destruction); + swap(update, other.update); + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever a new instance is created and assigned to an entity.
+ * Listeners are invoked after the object has been assigned to the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_construct() noexcept { + return sink{construction}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is explicitly updated.
+ * Listeners are invoked after the object has been updated. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_update() noexcept { + return sink{update}; + } + + /** + * @brief Returns a sink object. + * + * The sink returned by this function can be used to receive notifications + * whenever an instance is removed from an entity and thus destroyed.
+ * Listeners are invoked before the object has been removed from the entity. + * + * @sa sink + * + * @return A temporary sink object. + */ + [[nodiscard]] auto on_destroy() noexcept { + return sink{destruction}; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @return A return value as returned by the underlying storage. + */ + auto emplace() { + const auto entt = underlying_type::emplace(); + construction.publish(owner_or_assert(), entt); + return entt; + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam Args Types of arguments to forward to the underlying storage. + * @param hint A valid identifier. + * @param args Parameters to forward to the underlying storage. + * @return A return value as returned by the underlying storage. + */ + template + decltype(auto) emplace(const entity_type hint, Args &&...args) { + if constexpr(std::is_same_v) { + const auto entt = underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), entt); + return entt; + } else { + underlying_type::emplace(hint, std::forward(args)...); + construction.publish(owner_or_assert(), hint); + return this->get(hint); + } + } + + /** + * @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) { + underlying_type::patch(entt, std::forward(func)...); + update.publish(owner_or_assert(), entt); + return this->get(entt); + } + + /** + * @brief Emplace elements into a storage. + * + * The behavior of this operation depends on the underlying storage type + * (for example, components vs entities).
+ * Refer to the specific documentation for more details. + * + * @tparam It Iterator type (as required by the underlying storage type). + * @tparam Args Types of arguments to forward to the underlying storage. + * @param first An iterator to the first element of the range. + * @param last An iterator past the last element of the range. + * @param args Parameters to use to forward to the underlying storage. + */ + template + void insert(It first, It last, Args &&...args) { + underlying_type::insert(first, last, std::forward(args)...); + + if(auto ® = owner_or_assert(); !construction.empty()) { + for(; first != last; ++first) { + construction.publish(reg, *first); + } + } + } + + /** + * @brief Forwards variables to derived classes, if any. + * @param value A variable wrapped in an opaque container. + */ + void bind(any value) noexcept final { + auto *reg = any_cast(&value); + owner = reg ? reg : owner; + underlying_type::bind(std::move(value)); + } + +private: + basic_registry_type *owner; + sigh_type construction; + sigh_type destruction; + sigh_type update; +}; + +} // namespace entt + +#endif + +// #include "sparse_set.hpp" + +// #include "storage.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class registry_storage_iterator final { + template + friend class registry_storage_iterator; + + using mapped_type = std::remove_reference_t()->second)>; + +public: + using value_type = std::pair &>; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr registry_storage_iterator() noexcept + : it{} {} + + constexpr registry_storage_iterator(It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr registry_storage_iterator(const registry_storage_iterator &other) noexcept + : registry_storage_iterator{other.it} {} + + constexpr registry_storage_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr registry_storage_iterator operator++(int) noexcept { + registry_storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr registry_storage_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr registry_storage_iterator operator--(int) noexcept { + registry_storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr registry_storage_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr registry_storage_iterator operator+(const difference_type value) const noexcept { + registry_storage_iterator copy = *this; + return (copy += value); + } + + constexpr registry_storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr registry_storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, *it[value].second}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, *it->second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator==(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + + template + friend constexpr bool operator<(const registry_storage_iterator &, const registry_storage_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const registry_storage_iterator &lhs, const registry_storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class registry_context { + using alloc_traits = std::allocator_traits; + using allocator_type = typename alloc_traits::template rebind_alloc>>; + +public: + explicit registry_context(const allocator_type &allocator) + : ctx{allocator} {} + + template + Type &emplace_as(const id_type id, Args &&...args) { + return any_cast(ctx.try_emplace(id, std::in_place_type, std::forward(args)...).first->second); + } + + template + Type &emplace(Args &&...args) { + return emplace_as(type_id().hash(), std::forward(args)...); + } + + template + Type &insert_or_assign(const id_type id, Type &&value) { + return any_cast> &>(ctx.insert_or_assign(id, std::forward(value)).first->second); + } + + template + Type &insert_or_assign(Type &&value) { + return insert_or_assign(type_id().hash(), std::forward(value)); + } + + template + bool erase(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() && it->second.type() == type_id() ? (ctx.erase(it), true) : false; + } + + template + [[nodiscard]] const Type &get(const id_type id = type_id().hash()) const { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] Type &get(const id_type id = type_id().hash()) { + return any_cast(ctx.at(id)); + } + + template + [[nodiscard]] const Type *find(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] Type *find(const id_type id = type_id().hash()) { + const auto it = ctx.find(id); + return it != ctx.end() ? any_cast(&it->second) : nullptr; + } + + template + [[nodiscard]] bool contains(const id_type id = type_id().hash()) const { + const auto it = ctx.find(id); + return it != ctx.cend() && it->second.type() == type_id(); + } + +private: + dense_map, identity, std::equal_to, allocator_type> ctx; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Fast and reliable entity-component system. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_registry { + using base_type = basic_sparse_set; + + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + + // std::shared_ptr because of its type erased allocator which is useful here + using pool_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; + using group_container_type = dense_map, identity, std::equal_to, typename alloc_traits::template rebind_alloc>>>; + + template + [[nodiscard]] auto &assure([[maybe_unused]] const id_type id = type_hash::value()) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + return entities; + } else { + auto &cpool = pools[id]; + + if(!cpool) { + using storage_type = storage_for_type; + using alloc_type = typename storage_type::allocator_type; + + if constexpr(std::is_void_v && !std::is_constructible_v) { + // std::allocator has no cross constructors (waiting for C++20) + cpool = std::allocate_shared(get_allocator(), alloc_type{}); + } else { + cpool = std::allocate_shared(get_allocator(), get_allocator()); + } + + cpool->bind(forward_as_any(*this)); + } + + ENTT_ASSERT(cpool->type() == type_id(), "Unexpected type"); + return static_cast &>(*cpool); + } + } + + template + [[nodiscard]] const auto *assure([[maybe_unused]] const id_type id = type_hash::value()) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if constexpr(std::is_same_v) { + return &entities; + } else { + if(const auto it = pools.find(id); it != pools.cend()) { + ENTT_ASSERT(it->second->type() == type_id(), "Unexpected type"); + return static_cast *>(it->second.get()); + } + + return static_cast *>(nullptr); + } + } + + void rebind() { + entities.bind(forward_as_any(*this)); + + for(auto &&curr: pools) { + curr.second->bind(forward_as_any(*this)); + } + } + +public: + /*! @brief Entity traits. */ + using traits_type = typename base_type::traits_type; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = base_type; + /*! @brief Context type. */ + using context = internal::registry_context; + /*! @brief Iterable registry type. */ + using iterable = iterable_adaptor>; + /*! @brief Constant iterable registry type. */ + using const_iterable = iterable_adaptor>; + + /** + * @copybrief storage_for + * @tparam Type Storage value type, eventually const. + */ + template + using storage_for_type = typename storage_for>>::type; + + /*! @brief Default constructor. */ + basic_registry() + : basic_registry{allocator_type{}} {} + + /** + * @brief Constructs an empty registry with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_registry(const allocator_type &allocator) + : basic_registry{0u, allocator} {} + + /** + * @brief Allocates enough memory upon construction to store `count` pools. + * @param count The number of pools to allocate memory for. + * @param allocator The allocator to use. + */ + basic_registry(const size_type count, const allocator_type &allocator = allocator_type{}) + : vars{allocator}, + pools{allocator}, + groups{allocator}, + entities{allocator} { + pools.reserve(count); + rebind(); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_registry(basic_registry &&other) noexcept + : vars{std::move(other.vars)}, + pools{std::move(other.pools)}, + groups{std::move(other.groups)}, + entities{std::move(other.entities)} { + rebind(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This registry. + */ + basic_registry &operator=(basic_registry &&other) noexcept { + vars = std::move(other.vars); + pools = std::move(other.pools); + groups = std::move(other.groups); + entities = std::move(other.entities); + + rebind(); + + return *this; + } + + /** + * @brief Exchanges the contents with those of a given registry. + * @param other Registry to exchange the content with. + */ + void swap(basic_registry &other) { + using std::swap; + + swap(vars, other.vars); + swap(pools, other.pools); + swap(groups, other.groups); + swap(entities, other.entities); + + rebind(); + other.rebind(); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return entities.get_allocator(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a registry. + * + * The iterable object returns a pair that contains the name and a reference + * to the current storage. + * + * @return An iterable object to use to _visit_ the registry. + */ + [[nodiscard]] iterable storage() noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.begin()}, internal::registry_storage_iterator{pools.end()}}; + } + + /*! @copydoc storage */ + [[nodiscard]] const_iterable storage() const noexcept { + return iterable_adaptor{internal::registry_storage_iterator{pools.cbegin()}, internal::registry_storage_iterator{pools.cend()}}; + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] common_type *storage(const id_type id) { + return const_cast(std::as_const(*this).storage(id)); + } + + /** + * @brief Finds the storage associated with a given name, if any. + * @param id Name used to map the storage within the registry. + * @return A pointer to the storage if it exists, a null pointer otherwise. + */ + [[nodiscard]] const common_type *storage(const id_type id) const { + const auto it = pools.find(id); + return it == pools.cend() ? nullptr : it->second.get(); + } + + /** + * @brief Returns the storage for a given component type. + * @tparam Type Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ + template + storage_for_type &storage(const id_type id = type_hash::value()) { + return assure(id); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @param id Optional name used to map the storage within the registry. + * @return The storage for the given component type. + */ + template + const storage_for_type *storage(const id_type id = type_hash::value()) const { + return assure(id); + } + + /** + * @brief Checks if an identifier refers to a valid entity. + * @param entt An identifier, either valid or not. + * @return True if the identifier is valid, false otherwise. + */ + [[nodiscard]] bool valid(const entity_type entt) const { + return entities.contains(entt) && (entities.index(entt) < entities.free_list()); + } + + /** + * @brief Returns the actual version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if valid, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const { + return entities.current(entt); + } + + /** + * @brief Creates a new entity or recycles a destroyed one. + * @return A valid identifier. + */ + [[nodiscard]] entity_type create() { + return entities.emplace(); + } + + /** + * @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) { + return entities.emplace(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) { + entities.insert(std::move(first), std::move(last)); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * @warning + * Adding or removing components to an entity that is being destroyed can + * result in undefined behavior. + * + * @param entt A valid identifier. + * @return The version of the recycled entity. + */ + version_type destroy(const entity_type entt) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->remove(entt); + } + + entities.erase(entt); + return entities.current(entt); + } + + /** + * @brief Destroys an entity and releases its identifier. + * + * The suggested version or the valid version closest to the suggested one + * is used instead of the implicitly generated version. + * + * @sa destroy + * + * @param entt A valid identifier. + * @param version A desired version upon destruction. + * @return The version actually assigned to the entity. + */ + version_type destroy(const entity_type entt, const version_type version) { + destroy(entt); + const auto elem = traits_type::construct(traits_type::to_entity(entt), version); + return entities.bump((elem == tombstone) ? traits_type::next(elem) : elem); + } + + /** + * @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) { + entities.sort_as(first, last); + + const auto from = entities.cbegin(0); + const auto to = from + std::distance(first, last); + + for(auto &&curr: pools) { + curr.second->remove(from, to); + } + + entities.erase(from, to); + } + + /** + * @brief Assigns the given component to an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to assign a component to an entity that already owns it + * results in undefined behavior. + * + * @tparam Type Type of component to create. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace(const entity_type entt, Args &&...args) { + return assure().emplace(entt, std::forward(args)...); + } + + /** + * @brief Assigns each entity in a range the given component. + * + * @sa emplace + * + * @tparam Type Type of component to create. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param value An instance of the component to assign. + */ + template + void insert(It first, It last, const Type &value = {}) { + assure().insert(std::move(first), std::move(last), value); + } + + /** + * @brief Assigns each entity in a range the given components. + * + * @sa emplace + * + * @tparam Type Type of component to create. + * @tparam EIt Type of input iterator. + * @tparam CIt Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @param from An iterator to the first element of the range of components. + */ + template::value_type, Type>>> + void insert(EIt first, EIt last, CIt from) { + assure().insert(first, last, from); + } + + /** + * @brief Assigns or replaces the given component for an entity. + * + * @sa emplace + * @sa replace + * + * @tparam Type Type of component to assign or replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the newly created component. + */ + template + decltype(auto) emplace_or_replace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } else { + return cpool.emplace(entt, std::forward(args)...); + } + } + + /** + * @brief Patches the given component for an entity. + * + * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * @warning + * Attempting to patch a component of an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Type of component to patch. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + * @return A reference to the patched component. + */ + template + decltype(auto) patch(const entity_type entt, Func &&...func) { + return assure().patch(entt, std::forward(func)...); + } + + /** + * @brief Replaces the given component for an entity. + * + * The component must have a proper constructor or be of aggregate type. + * + * @warning + * Attempting to replace a component of an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Type of component to replace. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return A reference to the component being replaced. + */ + template + decltype(auto) replace(const entity_type entt, Args &&...args) { + return patch(entt, [&args...](auto &...curr) { ((curr = Type{std::forward(args)...}), ...); }); + } + + /** + * @brief Removes the given components from an entity. + * @tparam Type Type of component to remove. + * @tparam Other Other types of components to remove. + * @param entt A valid identifier. + * @return The number of components actually removed. + */ + template + size_type remove(const entity_type entt) { + return (assure().remove(entt) + ... + assure().remove(entt)); + } + + /** + * @brief Removes the given components from all the entities in a range. + * + * @sa remove + * + * @tparam Type Type of component to remove. + * @tparam Other Other types of components to remove. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of components actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + count += cpools[pos]->remove(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + count += std::apply([entt = *first](auto &...curr) { return (curr.remove(entt) + ... + 0u); }, cpools); + } + } + + return count; + } + + /** + * @brief Erases the given components from an entity. + * + * @warning + * Attempting to erase a component from an entity that doesn't own it + * results in undefined behavior. + * + * @tparam Type Types of components to erase. + * @tparam Other Other types of components to erase. + * @param entt A valid identifier. + */ + template + void erase(const entity_type entt) { + (assure().erase(entt), (assure().erase(entt), ...)); + } + + /** + * @brief Erases the given components from all the entities in a range. + * + * @sa erase + * + * @tparam Type Types of components to erase. + * @tparam Other Other types of components to erase. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + common_type *cpools[sizeof...(Other) + 1u]{&assure(), &assure()...}; + + for(size_type pos{}, len = sizeof...(Other) + 1u; pos < len; ++pos) { + if constexpr(sizeof...(Other) != 0u) { + if(cpools[pos]->data() == first.data()) { + std::swap(cpools[pos], cpools[sizeof...(Other)]); + } + } + + cpools[pos]->erase(first, last); + } + } else { + for(auto cpools = std::forward_as_tuple(assure(), assure()...); first != last; ++first) { + std::apply([entt = *first](auto &...curr) { (curr.erase(entt), ...); }, cpools); + } + } + } + + /** + * @brief Erases components satisfying specific criteria from an entity. + * + * The function type is equivalent to: + * + * @code{.cpp} + * void(const id_type, typename basic_registry::base_type &); + * @endcode + * + * Only storage where the entity exists are passed to the function. + * + * @tparam Func Type of the function object to invoke. + * @param entt A valid identifier. + * @param func A valid function object. + */ + template + void erase_if(const entity_type entt, Func func) { + for(auto [id, cpool]: storage()) { + if(cpool.contains(entt) && func(id, std::as_const(cpool))) { + cpool.erase(entt); + } + } + } + + /** + * @brief Removes all tombstones from a registry or only the pools for the + * given components. + * @tparam Type Types of components for which to clear all tombstones. + */ + template + void compact() { + if constexpr(sizeof...(Type) == 0u) { + for(auto &&curr: pools) { + curr.second->compact(); + } + } else { + (assure().compact(), ...); + } + } + + /** + * @brief Check if an entity is part of all the given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of all the storage, false otherwise. + */ + template + [[nodiscard]] bool all_of([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + auto *cpool = assure...>(); + return cpool && cpool->contains(entt); + } else { + return (all_of(entt) && ...); + } + } + + /** + * @brief Check if an entity is part of at least one given storage. + * @tparam Type Type of storage to check for. + * @param entt A valid identifier. + * @return True if the entity is part of at least one storage, false + * otherwise. + */ + template + [[nodiscard]] bool any_of([[maybe_unused]] const entity_type entt) const { + return (all_of(entt) || ...); + } + + /** + * @brief Returns references to the given components for an entity. + * + * @warning + * Attempting to get a component from an entity that doesn't own it results + * in undefined behavior. + * + * @tparam Type Types of components to get. + * @param entt A valid identifier. + * @return References to the components owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + return (assure>()->get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /*! @copydoc get */ + template + [[nodiscard]] decltype(auto) get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (static_cast &>(assure>()).get(entt), ...); + } else { + return std::forward_as_tuple(get(entt)...); + } + } + + /** + * @brief Returns a reference to the given component for an entity. + * + * In case the entity doesn't own the component, the parameters provided are + * used to construct it. + * + * @sa get + * @sa emplace + * + * @tparam Type Type of component to get. + * @tparam Args Types of arguments to use to construct the component. + * @param entt A valid identifier. + * @param args Parameters to use to initialize the component. + * @return Reference to the component owned by the entity. + */ + template + [[nodiscard]] decltype(auto) get_or_emplace(const entity_type entt, Args &&...args) { + if(auto &cpool = assure(); cpool.contains(entt)) { + return cpool.get(entt); + } else { + return cpool.emplace(entt, std::forward(args)...); + } + } + + /** + * @brief Returns pointers to the given components for an entity. + * + * @note + * The registry retains ownership of the pointed-to components. + * + * @tparam Type Types of components to get. + * @param entt A valid identifier. + * @return Pointers to the components owned by the entity. + */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) const { + if constexpr(sizeof...(Type) == 1u) { + const auto *cpool = assure...>(); + return (cpool && cpool->contains(entt)) ? std::addressof(cpool->get(entt)) : nullptr; + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /*! @copydoc try_get */ + template + [[nodiscard]] auto try_get([[maybe_unused]] const entity_type entt) { + if constexpr(sizeof...(Type) == 1u) { + return (const_cast(std::as_const(*this).template try_get(entt)), ...); + } else { + return std::make_tuple(try_get(entt)...); + } + } + + /** + * @brief Clears a whole registry or the pools for the given components. + * @tparam Type Types of components to remove from their entities. + */ + template + void clear() { + if constexpr(sizeof...(Type) == 0u) { + for(size_type pos = pools.size(); pos; --pos) { + pools.begin()[pos - 1u].second->clear(); + } + + const auto elem = entities.each(); + entities.erase(elem.begin().base(), elem.end().base()); + } else { + (assure().clear(), ...); + } + } + + /** + * @brief Checks if an entity has components assigned. + * @param entt A valid identifier. + * @return True if the entity has no components assigned, false otherwise. + */ + [[nodiscard]] bool orphan(const entity_type entt) const { + return std::none_of(pools.cbegin(), pools.cend(), [entt](auto &&curr) { return curr.second->contains(entt); }); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever a new instance of the + * given component is created and assigned to an entity.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** assigning the component to the entity. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_construct(const id_type id = type_hash::value()) { + return assure(id).on_construct(); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is explicitly updated.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **after** updating the component. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_update(const id_type id = type_hash::value()) { + return assure(id).on_update(); + } + + /** + * @brief Returns a sink object for the given component. + * + * Use this function to receive notifications whenever an instance of the + * given component is removed from an entity and thus destroyed.
+ * The function type for a listener is equivalent to: + * + * @code{.cpp} + * void(basic_registry &, Entity); + * @endcode + * + * Listeners are invoked **before** removing the component from the entity. + * + * @sa sink + * + * @tparam Type Type of component of which to get the sink. + * @param id Optional name used to map the storage within the registry. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto on_destroy(const id_type id = type_hash::value()) { + return assure(id).on_destroy(); + } + + /** + * @brief Returns a view for the given components. + * @tparam Type Type of component used to construct the view. + * @tparam Other Other types of components used to construct the view. + * @tparam Exclude Types of components used to filter the view. + * @return A newly created view. + */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) const { + const auto cpools = std::make_tuple(assure>(), assure>()..., assure>()...); + basic_view, storage_for_type...>, exclude_t...>> elem{}; + std::apply([&elem](const auto *...curr) { ((curr ? elem.storage(*curr) : void()), ...); }, cpools); + return elem; + } + + /*! @copydoc view */ + template + [[nodiscard]] basic_view, storage_for_type...>, exclude_t...>> + view(exclude_t = exclude_t{}) { + return {assure>(), assure>()..., assure>()...}; + } + + /** + * @brief Returns a group for the given components. + * @tparam Owned Types of storage _owned_ by the group. + * @tparam Get Types of storage _observed_ by the group, if any. + * @tparam Exclude Types of storage used to filter the group, if any. + * @return A newly created group. + */ + template + basic_group...>, get_t...>, exclude_t...>> + group(get_t = get_t{}, exclude_t = exclude_t{}) { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; + + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + std::shared_ptr handler{}; + + if constexpr(sizeof...(Owned) == 0u) { + handler = std::allocate_shared(get_allocator(), get_allocator(), assure>()..., assure>()...); + } else { + handler = std::allocate_shared(get_allocator(), assure>()..., assure>()..., assure>()...); + [[maybe_unused]] const id_type elem[]{type_hash>::value()..., type_hash>::value()..., type_hash>::value()...}; + ENTT_ASSERT(std::all_of(groups.cbegin(), groups.cend(), [&elem](const auto &data) { return data.second->owned(elem, sizeof...(Owned)) == 0u; }), "Conflicting groups"); + } + + groups.emplace(type_hash::value(), handler); + return {*handler}; + } + + /*! @copydoc group */ + template + basic_group...>, get_t...>, exclude_t...>> + group_if_exists(get_t = get_t{}, exclude_t = exclude_t{}) const { + using handler_type = typename basic_group...>, get_t...>, exclude_t...>>::handler; + + if(auto it = groups.find(type_hash::value()); it != groups.cend()) { + return {*std::static_pointer_cast(it->second)}; + } + + return {}; + } + + /** + * @brief Checks whether the given components belong to any group. + * @tparam Type Type of component in which one is interested. + * @tparam Other Other 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 { + const id_type elem[]{type_hash>::value(), type_hash>::value()...}; + return std::any_of(groups.cbegin(), groups.cend(), [&elem](auto &&data) { return data.second->owned(elem, 1u + sizeof...(Other)); }); + } + + /** + * @brief Sorts the elements of a given component. + * + * The comparison function object returns `true` if the first element is + * _less_ than the second one, `false` otherwise. Its signature is also + * equivalent to one of the following: + * + * @code{.cpp} + * bool(const Entity, const Entity); + * bool(const Type &, const Type &); + * @endcode + * + * Moreover, it shall induce a _strict weak ordering_ on the values.
+ * The sort function object offers an `operator()` that accepts: + * + * * An iterator to the first element of the range to sort. + * * An iterator past the last element of the range to sort. + * * A comparison function object to use to compare the elements. + * + * The comparison function object hasn't necessarily the type of the one + * passed along with the other parameters to this member function. + * + * @warning + * Pools of components owned by a group cannot be sorted. + * + * @tparam Type Type of components to sort. + * @tparam Compare Type of comparison function object. + * @tparam Sort Type of sort function object. + * @tparam Args Types of arguments to forward to the sort function object. + * @param compare A valid comparison function object. + * @param algo A valid sort function object. + * @param args Arguments to forward to the sort function object, if any. + */ + template + void sort(Compare compare, Sort algo = Sort{}, Args &&...args) { + ENTT_ASSERT(!owned(), "Cannot sort owned storage"); + auto &cpool = assure(); + + if constexpr(std::is_invocable_v) { + auto comp = [&cpool, compare = std::move(compare)](const auto lhs, const auto rhs) { return compare(std::as_const(cpool.get(lhs)), std::as_const(cpool.get(rhs))); }; + cpool.sort(std::move(comp), std::move(algo), std::forward(args)...); + } else { + cpool.sort(std::move(compare), std::move(algo), std::forward(args)...); + } + } + + /** + * @brief Sorts two pools of components in the same way. + * + * Entities and components in `To` which are part of both storage are sorted + * internally with the order they have in `From`. The others follow in no + * particular order. + * + * @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"); + const base_type &cpool = assure(); + assure().sort_as(cpool.begin(), cpool.end()); + } + + /** + * @brief Returns the context object, that is, a general purpose container. + * @return The context object, that is, a general purpose container. + */ + context &ctx() noexcept { + return vars; + } + + /*! @copydoc ctx */ + const context &ctx() const noexcept { + return vars; + } + +private: + context vars; + pool_container_type pools; + group_container_type groups; + storage_for_type entities; +}; + +} // namespace entt + +#endif + +// #include "entity/runtime_view.hpp" +#ifndef ENTT_ENTITY_RUNTIME_VIEW_HPP +#define ENTT_ENTITY_RUNTIME_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class runtime_view_iterator final { + using iterator_type = typename Set::iterator; + + [[nodiscard]] bool valid() const { + return (!tombstone_check || *it != tombstone) + && std::all_of(++pools->begin(), pools->end(), [entt = *it](const auto *curr) { return curr->contains(entt); }) + && std::none_of(filter->cbegin(), filter->cend(), [entt = *it](const auto *curr) { return curr && curr->contains(entt); }); + } + +public: + using difference_type = typename iterator_type::difference_type; + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using iterator_category = std::bidirectional_iterator_tag; + + constexpr runtime_view_iterator() noexcept + : pools{}, + filter{}, + it{}, + tombstone_check{} {} + + runtime_view_iterator(const std::vector &cpools, const std::vector &ignore, iterator_type curr) noexcept + : pools{&cpools}, + filter{&ignore}, + it{curr}, + tombstone_check{pools->size() == 1u && (*pools)[0u]->policy() == deletion_policy::in_place} { + if(it != (*pools)[0]->end() && !valid()) { + ++(*this); + } + } + + runtime_view_iterator &operator++() { + while(++it != (*pools)[0]->end() && !valid()) {} + return *this; + } + + runtime_view_iterator operator++(int) { + runtime_view_iterator orig = *this; + return ++(*this), orig; + } + + runtime_view_iterator &operator--() { + while(--it != (*pools)[0]->begin() && !valid()) {} + return *this; + } + + runtime_view_iterator operator--(int) { + runtime_view_iterator orig = *this; + return operator--(), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return it.operator->(); + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr bool operator==(const runtime_view_iterator &other) const noexcept { + return it == other.it; + } + + [[nodiscard]] constexpr bool operator!=(const runtime_view_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const std::vector *pools; + const std::vector *filter; + iterator_type it; + bool tombstone_check; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Generic runtime view. + * + * Runtime views iterate over those entities that are at least in the given + * storage. During initialization, a runtime view looks at the number of + * entities available for each component and uses the smallest set in order to + * get a performance boost when iterating. + * + * @b Important + * + * Iterators aren't invalidated if: + * + * * New elements are added to the storage. + * * The entity currently pointed is modified (for example, components are added + * or removed from it). + * * The entity currently pointed is destroyed. + * + * In all other cases, modifying the storage iterated by the view in any way + * invalidates all the iterators. + * + * @tparam Type Common base type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_runtime_view { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::runtime_view_iterator; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_runtime_view() noexcept + : basic_runtime_view{allocator_type{}} {} + + /** + * @brief Constructs an empty, invalid view with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_runtime_view(const allocator_type &allocator) + : pools{allocator}, + filter{allocator} {} + + /*! @brief Default copy constructor. */ + basic_runtime_view(const basic_runtime_view &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_runtime_view(const basic_runtime_view &other, const allocator_type &allocator) + : pools{other.pools, allocator}, + filter{other.filter, allocator} {} + + /*! @brief Default move constructor. */ + basic_runtime_view(basic_runtime_view &&) noexcept(std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_runtime_view(basic_runtime_view &&other, const allocator_type &allocator) + : pools{std::move(other.pools), allocator}, + filter{std::move(other.filter), allocator} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(const basic_runtime_view &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + basic_runtime_view &operator=(basic_runtime_view &&) noexcept(std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Exchanges the contents with those of a given view. + * @param other View to exchange the content with. + */ + void swap(basic_runtime_view &other) { + using std::swap; + swap(pools, other.pools); + swap(filter, other.filter); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.get_allocator(); + } + + /*! @brief Clears the view. */ + void clear() { + pools.clear(); + filter.clear(); + } + + /** + * @brief Appends an opaque storage object to a runtime view. + * @param base An opaque reference to a storage object. + * @return This runtime view. + */ + basic_runtime_view &iterate(common_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(common_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. + * + * 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. + * @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.
+ * The signature of the function should be equivalent to the following: + * + * @code{.cpp} + * void(const entity_type); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + for(const auto entity: *this) { + func(entity); + } + } + +private: + container_type pools; + container_type filter; +}; + +} // namespace entt + +#endif + +// #include "entity/snapshot.hpp" +#ifndef ENTT_ENTITY_SNAPSHOT_HPP +#define ENTT_ENTITY_SNAPSHOT_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "view.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +void orphans(Registry ®istry) { + auto &storage = registry.template storage(); + + for(auto entt: storage) { + if(registry.orphan(entt)) { + storage.erase(entt); + } + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Utility class to create snapshots from a registry. + * + * A _snapshot_ can be either a dump of the entire registry or a narrower + * selection of components of interest.
+ * This type can be used in both cases if provided with a correctly configured + * output archive. + * + * @tparam Registry Basic registry type. + */ +template +class basic_snapshot { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot(const registry_type &source) noexcept + : reg{&source} {} + + /*! @brief Default move constructor. */ + basic_snapshot(basic_snapshot &&) noexcept = default; + + /*! @brief Default move assignment operator. @return This snapshot. */ + basic_snapshot &operator=(basic_snapshot &&) noexcept = default; + + /** + * @brief Serializes all elements of a type with associated identifiers. + * @tparam Type Type of elements to serialize. + * @tparam Archive Type of output archive. + * @param archive A valid reference to an output archive. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, const id_type id = type_hash::value()) const { + if(const auto *storage = reg->template storage(id); storage) { + archive(static_cast(storage->size())); + + if constexpr(std::is_same_v) { + archive(static_cast(storage->free_list())); + + for(auto first = storage->data(), last = first + storage->size(); first != last; ++first) { + archive(*first); + } + } else { + for(auto elem: storage->reach()) { + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, elem); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + + /** + * @brief Serializes all elements of a type with associated identifiers for + * the entities in a range. + * @tparam Type Type of elements 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. + * @param id Optional name used to map the storage within the registry. + * @return An object of this type to continue creating the snapshot. + */ + template + const basic_snapshot &get(Archive &archive, It first, It last, const id_type id = type_hash::value()) const { + static_assert(!std::is_same_v, "Entity types not supported"); + + if(const auto *storage = reg->template storage(id); storage && !storage->empty()) { + archive(static_cast(std::distance(first, last))); + + for(; first != last; ++first) { + if(const auto entt = *first; storage->contains(entt)) { + archive(entt); + std::apply([&archive](auto &&...args) { (archive(std::forward(args)), ...); }, storage->get_as_tuple(entt)); + } else { + archive(static_cast(null)); + } + } + } else { + archive(typename traits_type::entity_type{}); + } + + return *this; + } + +private: + const registry_type *reg; +}; + +/** + * @brief Utility class to restore a snapshot as a whole. + * + * A snapshot loader requires that the destination registry be empty and loads + * all the data at once while keeping intact the identifiers that the entities + * originally had.
+ * An example of use is the implementation of a save/restore utility. + * + * @tparam Registry Basic registry type. + */ +template +class basic_snapshot_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_snapshot_loader(registry_type &source) noexcept + : reg{&source} { + // restoring a snapshot as a whole requires a clean registry + ENTT_ASSERT(reg->template storage().free_list() == 0u, "Registry must be empty"); + } + + /*! @brief Default move constructor. */ + basic_snapshot_loader(basic_snapshot_loader &&) noexcept = default; + + /*! @brief Default move assignment operator. @return This loader. */ + basic_snapshot_loader &operator=(basic_snapshot_loader &&) noexcept = default; + + /** + * @brief Restores all elements of a type with associated identifiers. + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_snapshot_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type count{}; + + storage.reserve(length); + archive(count); + + for(entity_type entity = null; length; --length) { + archive(entity); + storage.emplace(entity); + } + + storage.free_list(count); + } else { + auto &other = reg->template storage(); + entity_type entt{null}; + + while(length--) { + if(archive(entt); entt != null) { + const auto entity = other.contains(entt) ? entt : other.emplace(entt); + ENTT_ASSERT(entity == entt, "Entity not available for use"); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(entity); + } else { + Type elem{}; + archive(elem); + storage.emplace(entity, std::move(elem)); + } + } + } + } + + 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 function helps to identify and destroy those entities. + * + * @return A valid loader to continue restoring data. + */ + basic_snapshot_loader &orphans() { + internal::orphans(*reg); + return *this; + } + +private: + registry_type *reg; +}; + +/** + * @brief Utility class for _continuous loading_. + * + * A _continuous loader_ is designed to load data from a source registry to a + * (possibly) non-empty destination. The loader can accommodate in a registry + * more than one snapshot in a sort of _continuous loading_ that updates the + * destination one step at a time.
+ * Identifiers that entities originally had are not transferred to the target. + * Instead, the loader maps remote identifiers to local ones while restoring a + * snapshot.
+ * An example of use is the implementation of a client-server application with + * the requirement of transferring somehow parts of the representation side to + * side. + * + * @tparam Registry Basic registry type. + */ +template +class basic_continuous_loader { + static_assert(!std::is_const_v, "Non-const registry type required"); + using traits_type = typename Registry::traits_type; + + void restore(typename Registry::entity_type entt) { + if(const auto entity = to_entity(entt); remloc.contains(entity) && remloc[entity].first == entt) { + if(!reg->valid(remloc[entity].second)) { + remloc[entity].second = reg->create(); + } + } else { + remloc.insert_or_assign(entity, std::make_pair(entt, reg->create())); + } + } + + template + auto update(int, Container &container) -> decltype(typename Container::mapped_type{}, void()) { + // map like container + Container other; + + for(auto &&pair: container) { + using first_type = std::remove_const_t::first_type>; + using second_type = typename std::decay_t::second_type; + + if constexpr(std::is_same_v && std::is_same_v) { + other.emplace(map(pair.first), map(pair.second)); + } else if constexpr(std::is_same_v) { + other.emplace(map(pair.first), std::move(pair.second)); + } else { + static_assert(std::is_same_v, "Neither the key nor the value are of entity type"); + other.emplace(std::move(pair.first), map(pair.second)); + } + } + + using std::swap; + swap(container, other); + } + + template + auto update(char, Container &container) -> decltype(typename Container::value_type{}, void()) { + // vector like container + static_assert(std::is_same_v, "Invalid value type"); + + for(auto &&entt: container) { + entt = map(entt); + } + } + + template + void update([[maybe_unused]] Component &instance, [[maybe_unused]] Member Other::*member) { + if constexpr(!std::is_same_v) { + return; + } else if constexpr(std::is_same_v) { + instance.*member = map(instance.*member); + } else { + // maybe a container? let's try... + update(0, instance.*member); + } + } + +public: + /*! Basic registry type. */ + using registry_type = Registry; + /*! @brief Underlying entity identifier. */ + using entity_type = typename registry_type::entity_type; + + /** + * @brief Constructs an instance that is bound to a given registry. + * @param source A valid reference to a registry. + */ + basic_continuous_loader(registry_type &source) noexcept + : remloc{source.get_allocator()}, + 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 all elements of a type with associated identifiers. + * + * It creates local counterparts for remote elements as needed.
+ * Members are either data members of type entity_type or containers of + * entities. In both cases, a loader visits them and replaces entities with + * their local counterpart. + * + * @tparam Type Type of elements to restore. + * @tparam Archive Type of input archive. + * @param archive A valid reference to an input archive. + * @param id Optional name used to map the storage within the registry. + * @return A valid loader to continue restoring data. + */ + template + basic_continuous_loader &get(Archive &archive, const id_type id = type_hash::value()) { + auto &storage = reg->template storage(id); + typename traits_type::entity_type length{}; + entity_type entt{null}; + + archive(length); + + if constexpr(std::is_same_v) { + typename traits_type::entity_type in_use{}; + + storage.reserve(length); + archive(in_use); + + for(std::size_t pos{}; pos < in_use; ++pos) { + archive(entt); + restore(entt); + } + + for(std::size_t pos = in_use; pos < length; ++pos) { + archive(entt); + + if(const auto entity = to_entity(entt); remloc.contains(entity)) { + if(reg->valid(remloc[entity].second)) { + reg->destroy(remloc[entity].second); + } + + remloc.erase(entity); + } + } + } else { + for(auto &&ref: remloc) { + storage.remove(ref.second.second); + } + + while(length--) { + if(archive(entt); entt != null) { + restore(entt); + + if constexpr(std::tuple_size_v == 0u) { + storage.emplace(map(entt)); + } else { + Type elem{}; + archive(elem); + storage.emplace(map(entt), std::move(elem)); + } + } + } + } + + 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 function helps to identify and destroy those entities. + * + * @return A non-const reference to this loader. + */ + basic_continuous_loader &orphans() { + internal::orphans(*reg); + return *this; + } + + /** + * @brief Tests if a loader knows about a given entity. + * @param entt A valid identifier. + * @return True if `entity` is managed by the loader, false otherwise. + */ + [[nodiscard]] bool contains(entity_type entt) const noexcept { + const auto it = remloc.find(to_entity(entt)); + return it != remloc.cend() && it->second.first == entt; + } + + /** + * @brief Returns the identifier to which an entity refers. + * @param entt A valid identifier. + * @return The local identifier if any, the null entity otherwise. + */ + [[nodiscard]] entity_type map(entity_type entt) const noexcept { + if(const auto it = remloc.find(to_entity(entt)); it != remloc.cend() && it->second.first == entt) { + return it->second.second; + } + + return null; + } + +private: + dense_map> remloc; + registry_type *reg; +}; + +} // namespace entt + +#endif + +// #include "entity/sparse_set.hpp" +#ifndef ENTT_ENTITY_SPARSE_SET_HPP +#define ENTT_ENTITY_SPARSE_SET_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/algorithm.hpp" + +// #include "../core/any.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct sparse_set_iterator final { + using value_type = typename Container::value_type; + using pointer = typename Container::const_pointer; + using reference = typename Container::const_reference; + using difference_type = typename Container::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr sparse_set_iterator() noexcept + : packed{}, + offset{} {} + + constexpr sparse_set_iterator(const Container &ref, const difference_type idx) noexcept + : packed{std::addressof(ref)}, + offset{idx} {} + + constexpr sparse_set_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr sparse_set_iterator operator++(int) noexcept { + sparse_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr sparse_set_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr sparse_set_iterator operator--(int) noexcept { + sparse_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr sparse_set_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr sparse_set_iterator operator+(const difference_type value) const noexcept { + sparse_set_iterator copy = *this; + return (copy += value); + } + + constexpr sparse_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr sparse_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return packed->data()[index() - value]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return packed->data() + index(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer data() const noexcept { + return packed ? packed->data() : nullptr; + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + const Container *packed; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const sparse_set_iterator &lhs, const sparse_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @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 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. + * @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 underlying_type = typename entt_traits::entity_type; + + [[nodiscard]] auto sparse_ptr(const Entity entt) const { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + return (page < sparse.size() && sparse[page]) ? (sparse[page] + fast_mod(pos, traits_type::page_size)) : nullptr; + } + + [[nodiscard]] auto &sparse_ref(const Entity entt) const { + ENTT_ASSERT(sparse_ptr(entt), "Invalid element"); + const auto pos = static_cast(traits_type::to_entity(entt)); + return sparse[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + [[nodiscard]] auto to_iterator(const Entity entt) const { + return --(end() - index(entt)); + } + + [[nodiscard]] auto &assure_at_least(const Entity entt) { + const auto pos = static_cast(traits_type::to_entity(entt)); + const auto page = pos / traits_type::page_size; + + if(!(page < sparse.size())) { + sparse.resize(page + 1u, nullptr); + } + + if(!sparse[page]) { + constexpr entity_type init = null; + auto page_allocator{packed.get_allocator()}; + sparse[page] = alloc_traits::allocate(page_allocator, traits_type::page_size); + std::uninitialized_fill(sparse[page], sparse[page] + traits_type::page_size, init); + } + + return sparse[page][fast_mod(pos, traits_type::page_size)]; + } + + void release_sparse_pages() { + auto page_allocator{packed.get_allocator()}; + + for(auto &&page: sparse) { + if(page != nullptr) { + std::destroy(page, page + traits_type::page_size); + alloc_traits::deallocate(page_allocator, page, traits_type::page_size); + page = nullptr; + } + } + } + + void swap_at(const std::size_t from, const std::size_t to) { + auto &lhs = packed[from]; + auto &rhs = packed[to]; + + sparse_ref(lhs) = traits_type::combine(static_cast(to), traits_type::to_integral(lhs)); + sparse_ref(rhs) = traits_type::combine(static_cast(from), traits_type::to_integral(rhs)); + + std::swap(lhs, rhs); + } + + underlying_type policy_to_head() { + return traits_type::entity_mask * (mode != deletion_policy::swap_only); + } + +private: + virtual const void *get_at(const std::size_t) const { + return nullptr; + } + + virtual void swap_or_move([[maybe_unused]] const std::size_t lhs, [[maybe_unused]] const std::size_t rhs) { + ENTT_ASSERT((mode != deletion_policy::swap_only) || (((lhs < free_list()) + (rhs < free_list())) != 1u), "Cross swapping is not supported"); + } + +protected: + /*! @brief Random access iterator type. */ + using basic_iterator = internal::sparse_set_iterator; + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_only(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_only, "Deletion policy mismatch"); + const auto pos = static_cast(index(*it)); + bump(traits_type::next(*it)); + swap_at(pos, static_cast(head -= (pos < head))); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void swap_and_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::swap_and_pop, "Deletion policy mismatch"); + auto &self = sparse_ref(*it); + const auto entt = traits_type::to_entity(self); + sparse_ref(packed.back()) = traits_type::combine(entt, traits_type::to_integral(packed.back())); + packed[static_cast(entt)] = packed.back(); + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((packed.back() = null, true), ""); + // lazy self-assignment guard + self = null; + packed.pop_back(); + } + + /** + * @brief Erases an entity from a sparse set. + * @param it An iterator to the element to pop. + */ + void in_place_pop(const basic_iterator it) { + ENTT_ASSERT(mode == deletion_policy::in_place, "Deletion policy mismatch"); + const auto entt = traits_type::to_entity(std::exchange(sparse_ref(*it), null)); + packed[static_cast(entt)] = traits_type::combine(std::exchange(head, entt), tombstone); + } + +protected: + /** + * @brief Erases entities from a sparse set. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + virtual void pop(basic_iterator first, basic_iterator last) { + switch(mode) { + case deletion_policy::swap_and_pop: + for(; first != last; ++first) { + swap_and_pop(first); + } + break; + case deletion_policy::in_place: + for(; first != last; ++first) { + in_place_pop(first); + } + break; + case deletion_policy::swap_only: + for(; first != last; ++first) { + swap_only(first); + } + break; + } + } + + /*! @brief Erases all entities of a sparse set. */ + virtual void pop_all() { + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null)) { + for(auto first = begin(); !(first.index() < 0); ++first) { + if(*first != tombstone) { + sparse_ref(*first) = null; + } + } + break; + } + [[fallthrough]]; + case deletion_policy::swap_only: + case deletion_policy::swap_and_pop: + for(auto first = begin(); !(first.index() < 0); ++first) { + sparse_ref(*first) = null; + } + break; + } + + head = policy_to_head(); + packed.clear(); + } + + /** + * @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) { + auto &elem = assure_at_least(entt); + auto pos = size(); + + switch(mode) { + case deletion_policy::in_place: + if(head != traits_type::to_entity(null) && !force_back) { + pos = static_cast(head); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(head, traits_type::to_integral(entt)); + head = traits_type::to_entity(std::exchange(packed[pos], entt)); + break; + } + [[fallthrough]]; + case deletion_policy::swap_and_pop: + packed.push_back(entt); + ENTT_ASSERT(elem == null, "Slot not available"); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + break; + case deletion_policy::swap_only: + if(elem == null) { + packed.push_back(entt); + elem = traits_type::combine(static_cast(packed.size() - 1u), traits_type::to_integral(entt)); + } else { + ENTT_ASSERT(!(traits_type::to_entity(elem) < head), "Slot not available"); + bump(entt); + } + + if(force_back) { + pos = static_cast(head++); + swap_at(static_cast(traits_type::to_entity(elem)), pos); + } + + break; + } + + return --(end() - pos); + } + +public: + /*! @brief Entity traits. */ + using traits_type = entt_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = typename traits_type::value_type; + /*! @brief Underlying version type. */ + using version_type = typename traits_type::version_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @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 = std::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 elem 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 &elem, deletion_policy pol = deletion_policy::swap_and_pop, const allocator_type &allocator = {}) + : sparse{allocator}, + packed{allocator}, + info{&elem}, + mode{pol}, + head{policy_to_head()} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_sparse_set(basic_sparse_set &&other) noexcept + : sparse{std::move(other.sparse)}, + packed{std::move(other.packed)}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_sparse_set(basic_sparse_set &&other, const allocator_type &allocator) noexcept + : sparse{std::move(other.sparse), allocator}, + packed{std::move(other.packed), allocator}, + info{other.info}, + mode{other.mode}, + head{std::exchange(other.head, policy_to_head())} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + } + + /*! @brief Default destructor. */ + virtual ~basic_sparse_set() { + release_sparse_pages(); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This sparse set. + */ + basic_sparse_set &operator=(basic_sparse_set &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || packed.get_allocator() == other.packed.get_allocator(), "Copying a sparse set is not allowed"); + + release_sparse_pages(); + sparse = std::move(other.sparse); + packed = std::move(other.packed); + info = other.info; + mode = other.mode; + head = std::exchange(other.head, policy_to_head()); + 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(mode, other.mode); + swap(head, other.head); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return packed.get_allocator(); + } + + /** + * @brief Returns the deletion policy of a sparse set. + * @return The deletion policy of the sparse set. + */ + [[nodiscard]] deletion_policy policy() const noexcept { + return mode; + } + + /** + * @brief Returns the head of the free list, if any. + * @return The head of the free list. + */ + [[nodiscard]] size_type free_list() const noexcept { + return static_cast(head); + } + + /** + * @brief Sets the head of the free list, if possible. + * @param len The value to use as the new head of the free list. + */ + void free_list(const size_type len) noexcept { + ENTT_ASSERT((mode == deletion_policy::swap_only) && !(len > packed.size()), "Invalid value"); + head = static_cast(len); + } + + /** + * @brief Increases the capacity of a sparse set. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + virtual void reserve(const size_type cap) { + packed.reserve(cap); + } + + /** + * @brief Returns the number of elements that a sparse set has currently + * allocated space for. + * @return Capacity of the sparse set. + */ + [[nodiscard]] virtual size_type capacity() const noexcept { + return packed.capacity(); + } + + /*! @brief Requests the removal of unused capacity. */ + virtual void shrink_to_fit() { + packed.shrink_to_fit(); + } + + /** + * @brief Returns the extent of a sparse set. + * + * The extent of a sparse set is also the size of the internal sparse array. + * There is no guarantee that the internal packed array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Extent of the sparse set. + */ + [[nodiscard]] size_type extent() const noexcept { + return sparse.size() * traits_type::page_size; + } + + /** + * @brief Returns the number of elements in a sparse set. + * + * The number of elements is also the size of the internal packed array. + * There is no guarantee that the internal sparse array has the same size. + * Usually the size of the internal sparse array is equal or greater than + * the one of the internal packed array. + * + * @return Number of elements. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.size(); + } + + /** + * @brief Checks whether a sparse set is empty. + * @return True if the sparse set is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.empty(); + } + + /** + * @brief Checks whether a sparse set is fully packed. + * @return True if the sparse set is fully packed, false otherwise. + */ + [[nodiscard]] bool contiguous() const noexcept { + return (mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)); + } + + /** + * @brief Direct access to the internal packed array. + * @return A pointer to the internal packed array. + */ + [[nodiscard]] pointer data() const noexcept { + return packed.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * 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]] iterator begin() const noexcept { + const auto pos = static_cast(packed.size()); + return iterator{packed, pos}; + } + + /*! @copydoc begin */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last entity of a sparse + * set. + */ + [[nodiscard]] iterator end() const noexcept { + return iterator{packed, {}}; + } + + /*! @copydoc end */ + [[nodiscard]] const_iterator cend() const noexcept { + return end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the sparse set is empty, the returned iterator will be equal to + * `rend()`. + * + * @return An iterator to the first entity of the reversed internal packed + * array. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return std::make_reverse_iterator(end()); + } + + /*! @copydoc rbegin */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return rbegin(); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last entity of the + * reversed sparse set. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return std::make_reverse_iterator(begin()); + } + + /*! @copydoc rend */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return rend(); + } + + /*! @copydoc begin Useful only in case of swap-only policy. */ + [[nodiscard]] iterator begin(int) const noexcept { + return (mode == deletion_policy::swap_only) ? (end() - static_cast(head)) : begin(); + } + + /*! @copydoc cbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cbegin(int) const noexcept { + return begin(0); + } + + /*! @copydoc end Useful only in case of swap-only policy. */ + [[nodiscard]] iterator end(int) const noexcept { + return end(); + } + + /*! @copydoc cend Useful only in case of swap-only policy. */ + [[nodiscard]] const_iterator cend(int) const noexcept { + return end(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rbegin(int) const noexcept { + return std::make_reverse_iterator(end(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crbegin(int) const noexcept { + return rbegin(0); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] reverse_iterator rend(int) const noexcept { + return std::make_reverse_iterator(begin(0)); + } + + /*! @copydoc rbegin Useful only in case of swap-only policy. */ + [[nodiscard]] const_reverse_iterator crend(int) const noexcept { + return rend(0); + } + + /** + * @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]] const_iterator find(const entity_type entt) const noexcept { + return contains(entt) ? to_iterator(entt) : end(); + } + + /** + * @brief Checks if a sparse set contains an entity. + * @param entt A valid identifier. + * @return True if the sparse set contains the entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto cap = traits_type::entity_mask; + constexpr auto mask = traits_type::to_integral(null) & ~cap; + // testing versions permits to avoid accessing the packed array + return elem && (((mask & traits_type::to_integral(entt)) ^ traits_type::to_integral(*elem)) < cap); + } + + /** + * @brief Returns the contained version for an identifier. + * @param entt A valid identifier. + * @return The version for the given identifier if present, the tombstone + * version otherwise. + */ + [[nodiscard]] version_type current(const entity_type entt) const noexcept { + const auto elem = sparse_ptr(entt); + constexpr auto fallback = traits_type::to_version(tombstone); + return elem ? traits_type::to_version(*elem) : fallback; + } + + /** + * @brief Returns the position of an entity in a sparse set. + * + * @warning + * Attempting to get the position of an entity that doesn't belong to the + * sparse set results in undefined behavior. + * + * @param entt A valid identifier. + * @return The position of the entity in the sparse set. + */ + [[nodiscard]] size_type index(const entity_type entt) const noexcept { + ENTT_ASSERT(contains(entt), "Set does not contain entity"); + return static_cast(traits_type::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. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type at(const size_type pos) const noexcept { + return pos < packed.size() ? packed[pos] : null; + } + + /** + * @brief Returns the entity at specified location, without bounds checking. + * @param pos The position for which to return the entity. + * @return The entity at specified location. + */ + [[nodiscard]] entity_type operator[](const size_type pos) const noexcept { + ENTT_ASSERT(pos < packed.size(), "Position is out of bounds"); + return packed[pos]; + } + + /** + * @brief Returns the element assigned to an entity, if any. + * + * @warning + * Attempting to use an entity that doesn't belong to the sparse set results + * in undefined behavior. + * + * @param entt A valid identifier. + * @return An opaque pointer to the element assigned to the entity, if any. + */ + [[nodiscard]] const void *value(const entity_type entt) const noexcept { + return get_at(index(entt)); + } + + /*! @copydoc value */ + [[nodiscard]] void *value(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).value(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 elem Optional opaque element to forward to mixins, if any. + * @return Iterator pointing to the emplaced element in case of success, the + * `end()` iterator otherwise. + */ + iterator push(const entity_type entt, const void *elem = nullptr) { + return try_emplace(entt, (mode == deletion_policy::swap_only), elem); + } + + /** + * @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 push(It first, It last) { + for(auto it = first; it != last; ++it) { + try_emplace(*it, true); + } + + return first == last ? end() : find(*first); + } + + /** + * @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. + * @return The version of the given identifier. + */ + version_type bump(const entity_type entt) { + auto &entity = sparse_ref(entt); + ENTT_ASSERT(entt != tombstone && entity != null, "Cannot set the required version"); + entity = traits_type::combine(traits_type::to_integral(entity), traits_type::to_integral(entt)); + packed[static_cast(traits_type::to_entity(entity))] = entt; + return traits_type::to_version(entt); + } + + /** + * @brief Erases an entity from a sparse set. + * + * @warning + * Attempting to erase an entity that doesn't belong to the sparse set + * results in undefined behavior. + * + * @param entt A valid identifier. + */ + void erase(const entity_type entt) { + const auto it = to_iterator(entt); + pop(it, it + 1u); + } + + /** + * @brief Erases entities from a set. + * + * @sa erase + * + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void erase(It first, It last) { + if constexpr(std::is_same_v) { + pop(first, last); + } else { + for(; first != last; ++first) { + erase(*first); + } + } + } + + /** + * @brief Removes an entity from a sparse set if it exists. + * @param entt A valid identifier. + * @return True if the entity is actually removed, false otherwise. + */ + bool remove(const entity_type entt) { + return contains(entt) && (erase(entt), true); + } + + /** + * @brief Removes entities from a sparse set if they exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + * @return The number of entities actually removed. + */ + template + size_type remove(It first, It last) { + size_type count{}; + + if constexpr(std::is_same_v) { + while(first != last) { + while(first != last && !contains(*first)) { + ++first; + } + + const auto it = first; + + while(first != last && contains(*first)) { + ++first; + } + + count += std::distance(it, first); + erase(it, first); + } + } else { + for(; first != last; ++first) { + count += remove(*first); + } + } + + return count; + } + + /*! @brief Removes all tombstones from a sparse set. */ + void compact() { + if(mode == deletion_policy::in_place) { + size_type from = packed.size(); + for(; from && packed[from - 1u] == tombstone; --from) {} + underlying_type pos = std::exchange(head, traits_type::entity_mask); + + while(pos != traits_type::to_entity(null)) { + if(const auto to = static_cast(std::exchange(pos, traits_type::to_entity(packed[pos]))); to < from) { + --from; + swap_or_move(from, to); + + packed[to] = packed[from]; + const auto entity = static_cast(to); + sparse_ref(packed[to]) = traits_type::combine(entity, traits_type::to_integral(packed[to])); + + for(; from && packed[from - 1u] == tombstone; --from) {} + } + } + + packed.erase(packed.begin() + from, packed.end()); + } + } + + /** + * @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) { + const auto from = index(lhs); + const auto to = index(rhs); + + // basic no-leak guarantee if swapping throws + swap_or_move(from, to); + swap_at(from, 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((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + ENTT_ASSERT(!(length > packed.size()), "Length exceeds the number of elements"); + + 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_or_move(next, idx); + const auto entity = static_cast(curr); + sparse_ref(entt) = traits_type::combine(entity, traits_type::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) { + sort_n(static_cast(end(0) - begin(0)), std::move(compare), std::move(algo), std::forward(args)...); + } + + /** + * @brief Sort entities according to their order in a range. + * + * Entities that are part of both the sparse set and the range are ordered + * internally according to the order they have in the range.
+ * All other entities goes to the end of the sparse set and there are no + * guarantees on their order. + * + * @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 sort_as(It first, It last) { + ENTT_ASSERT((mode != deletion_policy::in_place) || (head == traits_type::to_entity(null)), "Sorting with tombstones not allowed"); + + for(auto it = begin(0); it.index() && first != last; ++first) { + if(const auto curr = *first; contains(curr)) { + if(const auto entt = *it; entt != curr) { + // basic no-leak guarantee (with invalid state) if swapping throws + swap_elements(entt, curr); + } + + ++it; + } + } + } + + /** + * @copybrief sort_as + * @param other The sparse sets that imposes the order of the entities. + */ + [[deprecated("use iterator based sort_as instead")]] void sort_as(const basic_sparse_set &other) { + sort_as(other.begin(), other.end()); + } + + /*! @brief Clears a sparse set. */ + void clear() { + pop_all(); + // sanity check to avoid subtle issues due to storage classes + ENTT_ASSERT((compact(), size()) == 0u, "Non-empty set"); + head = policy_to_head(); + packed.clear(); + } + + /** + * @brief Returned value type, if any. + * @return Returned value type, if any. + */ + const type_info &type() const noexcept { + return *info; + } + + /*! @brief Forwards variables to derived classes, if any. */ + virtual void bind(any) noexcept {} + +private: + sparse_container_type sparse; + packed_container_type packed; + const type_info *info; + deletion_policy mode; + underlying_type head; +}; + +} // 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/iterator.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_info.hpp" + +// #include "component.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + +// #include "sparse_set.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class storage_iterator final { + friend storage_iterator; + + using container_type = std::remove_const_t; + using alloc_traits = std::allocator_traits; + + using iterator_traits = std::iterator_traits, + typename alloc_traits::template rebind_traits::element_type>::const_pointer, + typename alloc_traits::template rebind_traits::element_type>::pointer>>; + +public: + using value_type = typename iterator_traits::value_type; + using pointer = typename iterator_traits::pointer; + using reference = typename iterator_traits::reference; + using difference_type = typename iterator_traits::difference_type; + using iterator_category = std::random_access_iterator_tag; + + constexpr storage_iterator() noexcept = default; + + constexpr storage_iterator(Container *ref, const difference_type idx) noexcept + : payload{ref}, + offset{idx} {} + + template, typename = std::enable_if_t> + constexpr storage_iterator(const storage_iterator, Size> &other) noexcept + : storage_iterator{other.payload, other.offset} {} + + constexpr storage_iterator &operator++() noexcept { + return --offset, *this; + } + + constexpr storage_iterator operator++(int) noexcept { + storage_iterator orig = *this; + return ++(*this), orig; + } + + constexpr storage_iterator &operator--() noexcept { + return ++offset, *this; + } + + constexpr storage_iterator operator--(int) noexcept { + storage_iterator orig = *this; + return operator--(), orig; + } + + constexpr storage_iterator &operator+=(const difference_type value) noexcept { + offset -= value; + return *this; + } + + constexpr storage_iterator operator+(const difference_type value) const noexcept { + storage_iterator copy = *this; + return (copy += value); + } + + constexpr storage_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr storage_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + const auto pos = index() - value; + return (*payload)[pos / Size][fast_mod(pos, Size)]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + const auto pos = index(); + return (*payload)[pos / Size] + fast_mod(pos, Size); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr difference_type index() const noexcept { + return offset - 1; + } + +private: + Container *payload; + difference_type offset; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs.index() - lhs.index(); +} + +template +[[nodiscard]] constexpr bool operator==(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return lhs.index() > rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator>(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const storage_iterator &lhs, const storage_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class extended_storage_iterator final { + template + friend class extended_storage_iterator; + +public: + using iterator_type = It; + using difference_type = std::ptrdiff_t; + 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 iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_storage_iterator() + : it{} {} + + constexpr extended_storage_iterator(iterator_type base, Other... other) + : it{base, other...} {} + + template && ...) && (std::is_constructible_v && ...)>> + constexpr extended_storage_iterator(const extended_storage_iterator &other) + : it{other.it} {} + + constexpr extended_storage_iterator &operator++() noexcept { + return ++std::get(it), (++std::get(it), ...), *this; + } + + constexpr extended_storage_iterator operator++(int) noexcept { + extended_storage_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {*std::get(it), *std::get(it)...}; + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return std::get(it); + } + + template + friend constexpr bool operator==(const extended_storage_iterator &, const extended_storage_iterator &) noexcept; + +private: + std::tuple it; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return std::get<0>(lhs.it) == std::get<0>(rhs.it); +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_storage_iterator &lhs, const extended_storage_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic storage implementation. + * + * Internal data structures arrange elements to maximize performance. There are + * no guarantees that objects are returned in the insertion order when iterate + * a storage. Do not make assumption on the order in any case. + * + * @warning + * Empty types aren't explicitly instantiated. Therefore, many of the functions + * normally available for non-empty types will not be available for empty ones. + * + * @tparam Type Type of objects assigned to the entities. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage: public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + [[nodiscard]] auto &element_at(const std::size_t pos) const { + return payload[pos / traits_type::page_size][fast_mod(pos, traits_type::page_size)]; + } + + auto assure_at_least(const std::size_t pos) { + const auto idx = pos / traits_type::page_size; + + if(!(idx < payload.size())) { + auto curr = payload.size(); + allocator_type allocator{get_allocator()}; + payload.resize(idx + 1u, nullptr); + + ENTT_TRY { + for(const auto last = payload.size(); curr < last; ++curr) { + payload[curr] = alloc_traits::allocate(allocator, traits_type::page_size); + } + } + ENTT_CATCH { + payload.resize(curr); + ENTT_THROW; + } + } + + return payload[idx] + fast_mod(pos, traits_type::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), get_allocator(), std::forward(args)...); + } + ENTT_CATCH { + base_type::pop(it, it + 1u); + ENTT_THROW; + } + + return it; + } + + void shrink_to_size(const std::size_t sz) { + const auto from = (sz + traits_type::page_size - 1u) / traits_type::page_size; + allocator_type allocator{get_allocator()}; + + for(auto pos = sz, length = base_type::size(); pos < length; ++pos) { + if constexpr(traits_type::in_place_delete) { + if(base_type::data()[pos] != tombstone) { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } else { + alloc_traits::destroy(allocator, std::addressof(element_at(pos))); + } + } + + for(auto pos = from, last = payload.size(); pos < last; ++pos) { + alloc_traits::deallocate(allocator, payload[pos], traits_type::page_size); + } + + payload.resize(from); + } + +private: + const void *get_at(const std::size_t pos) const final { + return std::addressof(element_at(pos)); + } + + void swap_or_move([[maybe_unused]] const std::size_t from, [[maybe_unused]] const std::size_t to) override { + static constexpr bool is_pinned_type_v = !(std::is_move_constructible_v && std::is_move_assignable_v); + // use a runtime value to avoid compile-time suppression that drives the code coverage tool crazy + ENTT_ASSERT((from + 1u) && !is_pinned_type_v, "Pinned type"); + + if constexpr(!is_pinned_type_v) { + auto &elem = element_at(from); + + if constexpr(traits_type::in_place_delete) { + if(base_type::operator[](to) == tombstone) { + allocator_type allocator{get_allocator()}; + entt::uninitialized_construct_using_allocator(to_address(assure_at_least(to)), allocator, std::move(elem)); + alloc_traits::destroy(allocator, std::addressof(elem)); + return; + } + } + + using std::swap; + swap(elem, element_at(to)); + } + } + +protected: + /** + * @brief Erases entities from a storage. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + void pop(underlying_iterator first, underlying_iterator last) override { + for(allocator_type allocator{get_allocator()}; first != last; ++first) { + // cannot use first.index() because it would break with cross iterators + auto &elem = element_at(base_type::index(*first)); + + if constexpr(traits_type::in_place_delete) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(elem)); + } else { + auto &other = element_at(base_type::size() - 1u); + // destroying on exit allows reentrant destructors + [[maybe_unused]] auto unused = std::exchange(elem, std::move(other)); + alloc_traits::destroy(allocator, std::addressof(other)); + base_type::swap_and_pop(first); + } + } + } + + /*! @brief Erases all entities of a storage. */ + void pop_all() override { + allocator_type allocator{get_allocator()}; + + for(auto first = base_type::begin(); !(first.index() < 0); ++first) { + if constexpr(traits_type::in_place_delete) { + if(*first != tombstone) { + base_type::in_place_pop(first); + alloc_traits::destroy(allocator, std::addressof(element_at(static_cast(first.index())))); + } + } else { + base_type::swap_and_pop(first); + alloc_traits::destroy(allocator, 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. + */ + underlying_iterator try_emplace([[maybe_unused]] const Entity entt, [[maybe_unused]] const bool force_back, const void *value) override { + if(value) { + if constexpr(std::is_copy_constructible_v) { + return emplace_element(entt, force_back, *static_cast(value)); + } else { + return base_type::end(); + } + } else { + if constexpr(std::is_default_constructible_v) { + return emplace_element(entt, force_back); + } else { + return base_type::end(); + } + } + } + +public: + /*! @brief Base type. */ + using base_type = underlying_type; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @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 Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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{traits_type::in_place_delete}, allocator}, + payload{allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)}, + payload{std::move(other.payload)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator}, + payload{std::move(other.payload), allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + } + + /*! @brief Default destructor. */ + ~basic_storage() override { + shrink_to_size(0u); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || payload.get_allocator() == other.payload.get_allocator(), "Copying a storage is not allowed"); + + shrink_to_size(0u); + base_type::operator=(std::move(other)); + payload = std::move(other.payload); + 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; + base_type::swap(other); + swap(payload, other.payload); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return payload.get_allocator(); + } + + /** + * @brief Increases the capacity of a storage. + * + * If the new capacity is greater than the current capacity, new storage is + * allocated, otherwise the method does nothing. + * + * @param cap Desired capacity. + */ + void reserve(const size_type cap) override { + if(cap != 0u) { + base_type::reserve(cap); + assure_at_least(cap - 1u); + } + } + + /** + * @brief Returns the number of elements that a storage has currently + * allocated space for. + * @return Capacity of the storage. + */ + [[nodiscard]] size_type capacity() const noexcept override { + return payload.size() * traits_type::page_size; + } + + /*! @brief Requests the removal of unused capacity. */ + void shrink_to_fit() override { + base_type::shrink_to_fit(); + shrink_to_size(base_type::size()); + } + + /** + * @brief Direct access to the array of objects. + * @return A pointer to the array of objects. + */ + [[nodiscard]] const_pointer raw() const noexcept { + return payload.data(); + } + + /*! @copydoc raw */ + [[nodiscard]] pointer raw() noexcept { + return payload.data(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + const auto pos = static_cast(base_type::size()); + return const_iterator{&payload, pos}; + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + const auto pos = static_cast(base_type::size()); + return iterator{&payload, pos}; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return const_iterator{&payload, {}}; + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return iterator{&payload, {}}; + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the storage is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Returns the object assigned to an entity. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return The object assigned to the entity. + */ + [[nodiscard]] const value_type &get(const entity_type entt) const noexcept { + return element_at(base_type::index(entt)); + } + + /*! @copydoc get */ + [[nodiscard]] value_type &get(const entity_type entt) noexcept { + return const_cast(std::as_const(*this).get(entt)); + } + + /** + * @brief Returns the object assigned to an entity as a tuple. + * @param entt A valid identifier. + * @return The object assigned to the entity as a tuple. + */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) const noexcept { + return std::forward_as_tuple(get(entt)); + } + + /*! @copydoc get_as_tuple */ + [[nodiscard]] std::tuple get_as_tuple(const entity_type entt) noexcept { + return std::forward_as_tuple(get(entt)); + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + * @param args Parameters to use to construct an object for the entity. + * @return A reference to the newly created object. + */ + template + value_type &emplace(const entity_type entt, Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_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. + * @return Iterator pointing to the last element inserted, if any. + */ + template + iterator insert(It first, It last, const value_type &value = {}) { + for(; first != last; ++first) { + emplace_element(*first, true, value); + } + + return begin(); + } + + /** + * @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. + * @return Iterator pointing to the first element inserted, if any. + */ + template::value_type, value_type>>> + iterator insert(EIt first, EIt last, CIt from) { + for(; first != last; ++first, ++from) { + emplace_element(*first, true, *from); + } + + return begin(); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity and + * a reference to its component. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(), begin()}, internal::extended_storage_iterator{base_type::end(), end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(), cbegin()}, internal::extended_storage_iterator{base_type::cend(), cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin(), rbegin()}, internal::extended_storage_iterator{base_type::rend(), rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin(), crbegin()}, internal::extended_storage_iterator{base_type::crend(), crend()}}; + } + +private: + container_type payload; +}; + +/*! @copydoc basic_storage */ +template +class basic_storage::page_size == 0u>> + : public basic_sparse_set::template rebind_alloc> { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set>; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Type; + /*! @brief Component traits. */ + using traits_type = component_traits; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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{traits_type::in_place_delete}, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + // std::allocator has no cross constructors (waiting for C++20) + if constexpr(std::is_void_v && !std::is_constructible_v) { + return allocator_type{}; + } else { + return allocator_type{base_type::get_allocator()}; + } + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + return std::tuple{}; + } + + /** + * @brief Assigns an entity to a storage and constructs its object. + * + * @warning + * Attempting to use an entity that already belongs to the storage results + * in undefined behavior. + * + * @tparam Args Types of arguments to use to construct the object. + * @param entt A valid identifier. + */ + template + void emplace(const entity_type entt, Args &&...) { + base_type::try_emplace(entt, false); + } + + /** + * @brief Updates the instance assigned to a given entity in-place. + * @tparam Func Types of the function objects to invoke. + * @param entt A valid identifier. + * @param func Valid function objects. + */ + template + void patch([[maybe_unused]] const entity_type entt, Func &&...func) { + ENTT_ASSERT(base_type::contains(entt), "Storage does not contain entity"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns entities to a storage. + * @tparam It Type of input iterator. + * @tparam Args Types of optional arguments. + * @param first An iterator to the first element of the range of entities. + * @param last An iterator past the last element of the range of entities. + */ + template + void insert(It first, It last, Args &&...) { + for(; first != last; ++first) { + base_type::try_emplace(*first, true); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin()}, internal::extended_storage_iterator{base_type::end()}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin()}, internal::extended_storage_iterator{base_type::cend()}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend()}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend()}}; + } +}; + +/** + * @brief Swap-only entity storage specialization. + * @tparam Entity A valid entity type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_storage + : public basic_sparse_set { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using underlying_type = basic_sparse_set>; + using underlying_iterator = typename underlying_type::basic_iterator; + + auto entity_at(const std::size_t pos) const noexcept { + ENTT_ASSERT(pos < underlying_type::traits_type::to_entity(null), "Invalid element"); + return underlying_type::traits_type::combine(static_cast(pos), {}); + } + +protected: + /** + * @brief Assigns an entity to a storage. + * @param hint A valid identifier. + * @return Iterator pointing to the emplaced element. + */ + underlying_iterator try_emplace(const Entity hint, const bool, const void *) override { + return base_type::find(emplace(hint)); + } + +public: + /*! @brief Base type. */ + using base_type = basic_sparse_set; + /*! @brief Type of the objects assigned to entities. */ + using value_type = Entity; + /*! @brief Underlying entity identifier. */ + using entity_type = Entity; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Extended iterable storage proxy. */ + using iterable = iterable_adaptor>; + /*! @brief Constant extended iterable storage proxy. */ + using const_iterable = iterable_adaptor>; + /*! @brief Extended reverse iterable storage proxy. */ + using reverse_iterable = iterable_adaptor>; + /*! @brief Constant extended reverse iterable storage proxy. */ + using const_reverse_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::swap_only, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_storage(basic_storage &&other) noexcept + : base_type{std::move(other)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_storage(basic_storage &&other, const allocator_type &allocator) noexcept + : base_type{std::move(other), allocator} {} + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This storage. + */ + basic_storage &operator=(basic_storage &&other) noexcept { + base_type::operator=(std::move(other)); + return *this; + } + + /** + * @brief Returns the object assigned to an entity, that is `void`. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + */ + void get([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + } + + /** + * @brief Returns an empty tuple. + * + * @warning + * Attempting to use an entity that doesn't belong to the storage results in + * undefined behavior. + * + * @param entt A valid identifier. + * @return Returns an empty tuple. + */ + [[nodiscard]] std::tuple<> get_as_tuple([[maybe_unused]] const entity_type entt) const noexcept { + ENTT_ASSERT(base_type::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + return std::tuple{}; + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * @return A valid identifier. + */ + entity_type emplace() { + const auto len = base_type::free_list(); + const auto entt = (len == base_type::size()) ? entity_at(len) : base_type::data()[len]; + return *base_type::try_emplace(entt, true); + } + + /** + * @brief Creates a new identifier or recycles a destroyed one. + * + * If the requested identifier isn't in use, the suggested one is used. + * Otherwise, a new identifier is returned. + * + * @param hint Required identifier. + * @return A valid identifier. + */ + entity_type emplace(const entity_type hint) { + if(hint == null || hint == tombstone) { + return emplace(); + } else if(const auto curr = underlying_type::traits_type::construct(underlying_type::traits_type::to_entity(hint), base_type::current(hint)); curr == tombstone) { + const auto pos = static_cast(underlying_type::traits_type::to_entity(hint)); + const auto entt = *base_type::try_emplace(hint, true); + + while(!(pos < base_type::size())) { + base_type::try_emplace(entity_at(base_type::size() - 1u), false); + } + + return entt; + } else if(const auto idx = base_type::index(curr); idx < base_type::free_list()) { + return emplace(); + } else { + return *base_type::try_emplace(hint, true); + } + } + + /** + * @brief Updates a given identifier. + * @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::index(entt) < base_type::free_list(), "The requested entity is not a live one"); + (std::forward(func)(), ...); + } + + /** + * @brief Assigns each element in a range an identifier. + * @tparam It Type of mutable 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 insert(It first, It last) { + for(const auto sz = base_type::size(); first != last && base_type::free_list() != sz; ++first) { + *first = *base_type::try_emplace(base_type::data()[base_type::free_list()], true); + } + + for(; first != last; ++first) { + *first = *base_type::try_emplace(entity_at(base_type::free_list()), true); + } + } + + /** + * @brief Makes all elements in a range contiguous. + * @tparam It Type of forward iterator. + * @param first An iterator to the first element of the range to pack. + * @param last An iterator past the last element of the range to pack. + * @return The number of elements within the newly created range. + */ + template + [[deprecated("use sort_as instead")]] size_type pack(It first, It last) { + base_type::sort_as(first, last); + return static_cast(std::distance(first, last)); + } + + /** + * @brief Returns the number of elements considered still in use. + * @return The number of elements considered still in use. + */ + [[deprecated("use free_list() instead")]] [[nodiscard]] size_type in_use() const noexcept { + return base_type::free_list(); + } + + /** + * @brief Sets the number of elements considered still in use. + * @param len The number of elements considered still in use. + */ + [[deprecated("use free_list(len) instead")]] void in_use(const size_type len) noexcept { + base_type::free_list(len); + } + + /** + * @brief Returns an iterable object to use to _visit_ a storage. + * + * The iterable object returns a tuple that contains the current entity. + * + * @return An iterable object to use to _visit_ the storage. + */ + [[nodiscard]] iterable each() noexcept { + return {internal::extended_storage_iterator{base_type::begin(0)}, internal::extended_storage_iterator{base_type::end(0)}}; + } + + /*! @copydoc each */ + [[nodiscard]] const_iterable each() const noexcept { + return {internal::extended_storage_iterator{base_type::cbegin(0)}, internal::extended_storage_iterator{base_type::cend(0)}}; + } + + /** + * @brief Returns a reverse iterable object to use to _visit_ a storage. + * + * @sa each + * + * @return A reverse iterable object to use to _visit_ the storage. + */ + [[nodiscard]] reverse_iterable reach() noexcept { + return {internal::extended_storage_iterator{base_type::rbegin()}, internal::extended_storage_iterator{base_type::rend(0)}}; + } + + /*! @copydoc reach */ + [[nodiscard]] const_reverse_iterable reach() const noexcept { + return {internal::extended_storage_iterator{base_type::crbegin()}, internal::extended_storage_iterator{base_type::crend(0)}}; + } +}; + +} // namespace entt + +#endif + +// #include "entity/view.hpp" +#ifndef ENTT_ENTITY_VIEW_HPP +#define ENTT_ENTITY_VIEW_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "../core/type_traits.hpp" + +// #include "entity.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] bool all_of_but(const std::size_t index, const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != index) && it[pos]->contains(entt); ++pos) {} + + if(pos == index) { + for(++pos; (pos != len) && it[pos]->contains(entt); ++pos) {} + } + + return pos == len; +} + +template +[[nodiscard]] bool none_of(const Type *const *it, const std::size_t len, const Entity entt) noexcept { + std::size_t pos{}; + for(; (pos != len) && !(it[pos] && it[pos]->contains(entt)); ++pos) {} + return pos == len; +} + +template +[[nodiscard]] bool fully_initialized(const Type *const *it, const std::size_t len) noexcept { + std::size_t pos{}; + for(; (pos != len) && it[pos]; ++pos) {} + return pos == len; +} + +template +[[nodiscard]] Result view_pack(const View &view, const Other &other, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence) { + Result elem{}; + // friend-initialization, avoid multiple calls to refresh + elem.pools = {view.template storage()..., other.template storage()...}; + elem.filter = {view.template storage()..., other.template storage()...}; + elem.refresh(); + return elem; +} + +template +class view_iterator final { + using iterator_type = typename Type::const_iterator; + + [[nodiscard]] bool valid(const typename iterator_type::value_type entt) const noexcept { + return ((Get != 1u) || (entt != tombstone)) && all_of_but(index, pools.data(), Get, entt) && none_of(filter.data(), Exclude, entt); + } + +public: + using value_type = typename iterator_type::value_type; + using pointer = typename iterator_type::pointer; + using reference = typename iterator_type::reference; + using difference_type = typename iterator_type::difference_type; + using iterator_category = std::forward_iterator_tag; + + constexpr view_iterator() noexcept + : it{}, + last{}, + pools{}, + filter{}, + index{} {} + + view_iterator(iterator_type curr, iterator_type to, std::array value, std::array excl, const std::size_t idx) noexcept + : it{curr}, + last{to}, + pools{value}, + filter{excl}, + index{idx} { + while(it != last && !valid(*it)) { + ++it; + } + } + + view_iterator &operator++() noexcept { + while(++it != last && !valid(*it)) {} + return *this; + } + + view_iterator operator++(int) noexcept { + view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] pointer operator->() const noexcept { + return &*it; + } + + [[nodiscard]] reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr bool operator==(const view_iterator &, const view_iterator &) noexcept; + +private: + iterator_type it; + iterator_type last; + std::array pools; + std::array filter; + std::size_t index; +}; + +template +[[nodiscard]] constexpr bool operator==(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const view_iterator &lhs, const view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +struct extended_view_iterator final { + using iterator_type = It; + 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; + using iterator_concept = std::forward_iterator_tag; + + constexpr extended_view_iterator() + : it{}, + pools{} {} + + extended_view_iterator(iterator_type from, std::tuple value) + : it{from}, + pools{value} {} + + extended_view_iterator &operator++() noexcept { + return ++it, *this; + } + + extended_view_iterator operator++(int) noexcept { + extended_view_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] reference operator*() const noexcept { + return std::apply([entt = *it](auto *...curr) { return std::tuple_cat(std::make_tuple(entt), curr->get_as_tuple(entt)...); }, pools); + } + + [[nodiscard]] pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr iterator_type base() const noexcept { + return it; + } + + template + friend bool constexpr operator==(const extended_view_iterator &, const extended_view_iterator &) noexcept; + +private: + It it; + std::tuple pools; +}; + +template +[[nodiscard]] constexpr bool operator==(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const extended_view_iterator &lhs, const extended_view_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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. + * + * @b Important + * + * View iterators aren't invalidated if: + * + * * New elements are added to the storage iterated by the view. + * * The entity currently returned is modified (for example, components are + * added or removed from it). + * * The entity currently returned is destroyed. + * + * In all other cases, modifying the storage iterated by a view in any way can + * invalidate all iterators. + */ +template +class basic_view; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + * @tparam Get Number of storage iterated by the view. + * @tparam Exclude Number of storage used to filter the view. + */ +template +class basic_common_view { + template + friend Return internal::view_pack(const View &, const Other &, std::index_sequence, std::index_sequence, std::index_sequence, std::index_sequence); + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_common_view() noexcept = default; + + basic_common_view(std::array value, std::array excl) noexcept + : pools{value}, + filter{excl}, + leading{}, + index{Get} { + unchecked_refresh(); + } + + void use(const std::size_t pos) noexcept { + if(leading) { + index = pos; + leading = pools[index]; + } + } + + void unchecked_refresh() noexcept { + index = 0u; + + for(size_type pos{1u}; pos < Get; ++pos) { + if(pools[pos]->size() < pools[index]->size()) { + index = pos; + } + } + + leading = pools[index]; + } + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename Type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Bidirectional iterator type. */ + using iterator = internal::view_iterator; + + /*! @brief Updates the internal leading view if required. */ + void refresh() noexcept { + size_type pos = (leading != nullptr) * Get; + for(; pos < Get && pools[pos] != nullptr; ++pos) {} + + if(pos == Get) { + unchecked_refresh(); + } + } + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Estimates the number of entities iterated by the view. + * @return Estimated number of entities iterated by the view. + */ + [[nodiscard]] size_type size_hint() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? iterator{leading->begin(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? iterator{leading->end(0), leading->end(0), pools, filter, index} : iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + const auto it = begin(); + return it != end() ? *it : null; + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + if(leading) { + auto it = leading->rbegin(0); + const auto last = leading->rend(0); + for(; it != last && !contains(*it); ++it) {} + return it == last ? null : *it; + } + + return null; + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return contains(entt) ? iterator{leading->find(entt), leading->end(), pools, filter, index} : end(); + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return leading && internal::fully_initialized(filter.data(), Exclude); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + if(leading) { + const auto idx = leading->find(entt).index(); + return (!(idx < 0 || idx > leading->begin(0).index())) && internal::all_of_but(index, pools.data(), Get, entt) && internal::none_of(filter.data(), Exclude, entt); + } + + return false; + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + std::array pools{}; + std::array filter{}; + const common_type *leading{}; + size_type index{Get}; + /*! @endcond */ +}; + +/** + * @brief General purpose view. + * + * This view visits all entities that are at least in the given storage. During + * initialization, it also looks at the number of elements available for each + * storage and uses the smallest set in order to get a performance boost. + * + * @sa basic_view + * + * @tparam Get Types of storage iterated by the view. + * @tparam Exclude Types of storage used to filter the view. + */ +template +class basic_view, exclude_t>: public basic_common_view, sizeof...(Get), sizeof...(Exclude)> { + using base_type = basic_common_view, sizeof...(Get), sizeof...(Exclude)>; + + template + static constexpr std::size_t index_of = type_list_index_v, type_list>; + + template + auto storage(std::index_sequence) const noexcept { + return std::make_tuple(storage()...); + } + + template + [[nodiscard]] auto dispatch_get(const std::tuple &curr) const { + if constexpr(Curr == Other) { + return std::forward_as_tuple(std::get(curr)...); + } else { + return storage()->get_as_tuple(std::get<0>(curr)); + } + } + + template + void each(Func &func, std::index_sequence) const { + for(const auto curr: storage()->each()) { + if(const auto entt = std::get<0>(curr); ((sizeof...(Get) != 1u) || (entt != tombstone)) && internal::all_of_but(this->index, this->pools.data(), sizeof...(Get), entt) && internal::none_of(this->filter.data(), sizeof...(Exclude), entt)) { + if constexpr(is_applicable_v{}, std::declval().get({})))>) { + std::apply(func, std::tuple_cat(std::make_tuple(entt), dispatch_get(curr)...)); + } else { + std::apply(func, std::tuple_cat(dispatch_get(curr)...)); + } + } + } + } + + template + void pick_and_each(Func &func, std::index_sequence seq) const { + ((storage() == base_type::handle() ? each(func, seq) : void()), ...); + } + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Bidirectional iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Iterable view type. */ + using iterable = iterable_adaptor>; + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(Get &...value, Exclude &...excl) noexcept + : base_type{{&value...}, {&excl...}} { + } + + /** + * @brief Constructs a view from a set of storage classes. + * @param value The storage for the types to iterate. + * @param excl The storage for the types used to filter the view. + */ + basic_view(std::tuple value, std::tuple excl = {}) noexcept + : basic_view{std::make_from_tuple(std::tuple_cat(value, excl))} {} + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Type Type of component to use to drive iterations. + */ + template + void use() noexcept { + use>(); + } + + /** + * @brief Forces a view to use a given component to drive iterations + * @tparam Index Index of the component to use to drive iterations. + */ + template + void use() noexcept { + base_type::use(Index); + } + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + return storage>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + using type = type_list_element_t>; + + if constexpr(Index < sizeof...(Get)) { + return static_cast(const_cast *>(this->pools[Index])); + } else { + return static_cast(const_cast *>(this->filter[Index - sizeof...(Get)])); + } + } + + /** + * @brief Assigns a storage to a view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + storage>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @tparam Type Type of storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Type &elem) noexcept { + static_assert(std::is_convertible_v> &>, "Unexpected type"); + + if constexpr(Index < sizeof...(Get)) { + this->pools[Index] = &elem; + base_type::refresh(); + } else { + this->filter[Index - sizeof...(Get)] = &elem; + } + } + + /** + * @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 Returns the components assigned to the given entity. + * @tparam Type Type of the component to get. + * @tparam Other Other 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 { + return get, index_of...>(entt); + } + + /** + * @brief Returns the components assigned to the given entity. + * @tparam Index Indexes of the components to get. + * @param entt A valid identifier. + * @return The components assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return std::apply([entt](auto *...curr) { return std::tuple_cat(curr->get_as_tuple(entt)...); }, storage(std::index_sequence_for{})); + } else if constexpr(sizeof...(Index) == 1) { + return (storage()->get(entt), ...); + } else { + return std::tuple_cat(storage()->get_as_tuple(entt)...); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @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 { + if(base_type::handle() != nullptr) { + pick_and_each(func, std::index_sequence_for{}); + } + } + + /** + * @brief Returns an iterable object to use to _visit_ a view. + * + * The iterable object returns a tuple that contains the current entity and + * a set of references to its non-empty components. The _constness_ of the + * components is as requested. + * + * @return An iterable object to use to _visit_ the view. + */ + [[nodiscard]] iterable each() const noexcept { + const auto as_pools = storage(std::index_sequence_for{}); + return {internal::extended_view_iterator{base_type::begin(), as_pools}, internal::extended_view_iterator{base_type::end(), as_pools}}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Basic storage view implementation. + * @warning For internal use only, backward compatibility not guaranteed. + * @tparam Type Common type among all storage types. + */ +template +class basic_storage_view { +protected: + /*! @cond TURN_OFF_DOXYGEN */ + basic_storage_view() noexcept = default; + + basic_storage_view(const Type *value) noexcept + : leading{value} {} + /*! @endcond */ + +public: + /*! @brief Common type among all storage types. */ + using common_type = Type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename common_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Random access iterator type. */ + using iterator = typename common_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename common_type::reverse_iterator; + + /** + * @brief Returns the leading storage of a view, if any. + * @return The leading storage of the view. + */ + [[nodiscard]] const common_type *handle() const noexcept { + return leading; + } + + /** + * @brief Returns the number of entities that have the given component. + * @return Number of entities that have the given component. + */ + [[nodiscard]] size_type size() const noexcept { + return leading ? leading->size() : size_type{}; + } + + /** + * @brief Checks whether a view is empty. + * @return True if the view is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return !leading || leading->empty(); + } + + /** + * @brief Returns an iterator to the first entity of the view. + * + * If the view is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first entity of the view. + */ + [[nodiscard]] iterator begin() const noexcept { + return leading ? leading->begin() : iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the view. + * @return An iterator to the entity following the last entity of the view. + */ + [[nodiscard]] iterator end() const noexcept { + return leading ? leading->end() : iterator{}; + } + + /** + * @brief Returns an iterator to the first entity of the reversed view. + * + * If the view is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first entity of the reversed view. + */ + [[nodiscard]] reverse_iterator rbegin() const noexcept { + return leading ? leading->rbegin() : reverse_iterator{}; + } + + /** + * @brief Returns an iterator that is past the last entity of the reversed + * view. + * @return An iterator to the entity following the last entity of the + * reversed view. + */ + [[nodiscard]] reverse_iterator rend() const noexcept { + return leading ? leading->rend() : reverse_iterator{}; + } + + /** + * @brief Returns the first entity of the view, if any. + * @return The first entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type front() const noexcept { + return empty() ? null : *leading->begin(); + } + + /** + * @brief Returns the last entity of the view, if any. + * @return The last entity of the view if one exists, the null entity + * otherwise. + */ + [[nodiscard]] entity_type back() const noexcept { + return empty() ? null : *leading->rbegin(); + } + + /** + * @brief Finds an entity. + * @param entt A valid identifier. + * @return An iterator to the given entity if it's found, past the end + * iterator otherwise. + */ + [[nodiscard]] iterator find(const entity_type entt) const noexcept { + return leading ? leading->find(entt) : iterator{}; + } + + /** + * @brief Checks if a view is fully initialized. + * @return True if the view is fully initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (leading != nullptr); + } + + /** + * @brief Checks if a view contains an entity. + * @param entt A valid identifier. + * @return True if the view contains the given entity, false otherwise. + */ + [[nodiscard]] bool contains(const entity_type entt) const noexcept { + return leading && leading->contains(entt); + } + +protected: + /*! @cond TURN_OFF_DOXYGEN */ + const common_type *leading{}; + /*! @endcond */ +}; + +/** + * @brief Storage view specialization. + * + * This specialization offers a boost in terms of performance. It can access the + * underlying data structure directly and avoid superfluous checks. + * + * @sa basic_view + * + * @tparam Get Type of storage iterated by the view. + */ +template +class basic_view, exclude_t<>, std::void_t>>: public basic_storage_view { + using base_type = basic_storage_view; + +public: + /*! @brief Common type among all storage types. */ + using common_type = typename base_type::common_type; + /*! @brief Underlying entity identifier. */ + using entity_type = typename base_type::entity_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Random access iterator type. */ + using iterator = typename base_type::iterator; + /*! @brief Reversed iterator type. */ + using reverse_iterator = typename base_type::reverse_iterator; + /*! @brief Iterable view type. */ + using iterable = decltype(std::declval().each()); + + /*! @brief Default constructor to use to create empty, invalid views. */ + basic_view() noexcept + : base_type{} {} + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(Get &value) noexcept + : base_type{&value} { + } + + /** + * @brief Constructs a view from a storage class. + * @param value The storage for the type to iterate. + */ + basic_view(std::tuple value, std::tuple<> = {}) noexcept + : basic_view{std::get<0>(value)} {} + + /** + * @brief Returns the storage for a given component type, if any. + * @tparam Type Type of component of which to return the storage. + * @return The storage for the given component type. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return storage<0>(); + } + + /** + * @brief Returns the storage for a given index, if any. + * @tparam Index Index of the storage to return. + * @return The storage for the given index. + */ + template + [[nodiscard]] auto *storage() const noexcept { + static_assert(Index == 0u, "Index out of bounds"); + return static_cast(const_cast *>(this->leading)); + } + + /** + * @brief Assigns a storage to a view. + * @param elem A storage to assign to the view. + */ + void storage(Get &elem) noexcept { + storage<0>(elem); + } + + /** + * @brief Assigns a storage to a view. + * @tparam Index Index of the storage to assign to the view. + * @param elem A storage to assign to the view. + */ + template + void storage(Get &elem) noexcept { + static_assert(Index == 0u, "Index out of bounds"); + this->leading = &elem; + } + + /** + * @brief Returns the component assigned to the given entity. + * @param entt A valid identifier. + * @return The component assigned to the given entity. + */ + [[nodiscard]] decltype(auto) operator[](const entity_type entt) const { + return storage()->get(entt); + } + + /** + * @brief Returns the identifier that occupies the given position. + * @param pos Position of the element to return. + * @return The identifier that occupies the given position. + */ + [[deprecated("use .begin()[pos] instead")]] [[nodiscard]] entity_type operator[](const size_type pos) const { + return base_type::begin()[pos]; + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Elem Type 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 { + static_assert(std::is_same_v, typename Get::value_type>, "Invalid component type"); + return get<0>(entt); + } + + /** + * @brief Returns the component assigned to the given entity. + * @tparam Index Index of the component to get. + * @param entt A valid identifier. + * @return The component assigned to the entity. + */ + template + [[nodiscard]] decltype(auto) get(const entity_type entt) const { + if constexpr(sizeof...(Index) == 0) { + return storage()->get_as_tuple(entt); + } else { + return storage()->get(entt); + } + } + + /** + * @brief Iterates entities and components and applies the given function + * object to them. + * + * The signature of the function must be equivalent to one of the following + * (non-empty types only, constness as requested): + * + * @code{.cpp} + * void(const entity_type, Type &); + * void(typename Type &); + * @endcode + * + * @tparam Func Type of the function object to invoke. + * @param func A valid function object. + */ + template + void each(Func func) const { + if(auto *elem = storage(); elem) { + if constexpr(is_applicable_veach().begin())>) { + for(const auto pack: elem->each()) { + std::apply(func, pack); + } + } else if constexpr(std::is_invocable_vbegin())>) { + for(auto &&component: *elem) { + func(component); + } + } else { + for(size_type pos = elem->size(); pos; --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 noexcept { + auto *elem = storage(); + return elem ? elem->each() : iterable{}; + } + + /** + * @brief Combines two views in a _more specific_ one. + * @tparam OGet Component list of the view to combine with. + * @tparam OExclude Filter list of the view to combine with. + * @param other The view to combine with. + * @return A more specific view. + */ + template + [[nodiscard]] auto operator|(const basic_view, exclude_t> &other) const noexcept { + return internal::view_pack, exclude_t>>( + *this, other, std::index_sequence_for{}, std::index_sequence_for<>{}, std::index_sequence_for{}, std::index_sequence_for{}); + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of storage classes used to create the view. + * @param storage The storage for the types to iterate. + */ +template +basic_view(Type &...storage) -> basic_view, exclude_t<>>; + +/** + * @brief Deduction guide. + * @tparam Get Types of components iterated by the view. + * @tparam Exclude Types of components used to filter the view. + */ +template +basic_view(std::tuple, std::tuple = {}) -> basic_view, exclude_t>; + +} // namespace entt + +#endif + +// #include "graph/adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_GRAPH_FWD_HPP +#define ENTT_GRAPH_FWD_HPP + +#include +#include +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Undirected graph category tag. */ +struct directed_tag {}; + +/*! @brief Directed graph category tag. */ +struct undirected_tag: directed_tag {}; + +template> +class adjacency_matrix; + +template> +class basic_flow; + +/*! @brief Alias declaration for the most common use case. */ +using flow = basic_flow<>; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "graph/dot.hpp" +#ifndef ENTT_GRAPH_DOT_HPP +#define ENTT_GRAPH_DOT_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @tparam Writer Vertex decorator type. + * @param out A standard output stream. + * @param graph The graph to output. + * @param writer Vertex decorator object. + */ +template +void dot(std::ostream &out, const Graph &graph, Writer writer) { + static_assert(std::is_base_of_v, "Invalid graph category"); + + if constexpr(std::is_same_v) { + out << "graph{"; + } else { + out << "digraph{"; + } + + for(auto &&vertex: graph.vertices()) { + out << vertex << "["; + writer(out, vertex); + out << "];"; + } + + for(auto [lhs, rhs]: graph.edges()) { + if constexpr(std::is_same_v) { + out << lhs << "--" << rhs << ";"; + } else { + out << lhs << "->" << rhs << ";"; + } + } + + out << "}"; +} + +/** + * @brief Outputs a graph in dot format. + * @tparam Graph Graph type, valid as long as it exposes edges and vertices. + * @param out A standard output stream. + * @param graph The graph to output. + */ +template +void dot(std::ostream &out, const Graph &graph) { + return dot(out, graph, [](auto &&...) {}); +} + +} // namespace entt + +#endif + +// #include "graph/flow.hpp" +#ifndef ENTT_GRAPH_FLOW_HPP +#define ENTT_GRAPH_FLOW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "adjacency_matrix.hpp" +#ifndef ENTT_GRAPH_ADJACENCY_MATRIX_HPP +#define ENTT_GRAPH_ADJACENCY_MATRIX_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/iterator.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class edge_iterator { + using size_type = std::size_t; + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr edge_iterator() noexcept + : it{}, + vert{}, + pos{}, + last{}, + offset{} {} + + constexpr edge_iterator(It base, const size_type vertices, const size_type from, const size_type to, const size_type step) noexcept + : it{std::move(base)}, + vert{vertices}, + pos{from}, + last{to}, + offset{step} { + for(; pos != last && !it[pos]; pos += offset) {} + } + + constexpr edge_iterator &operator++() noexcept { + for(pos += offset; pos != last && !it[pos]; pos += offset) {} + return *this; + } + + constexpr edge_iterator operator++(int) noexcept { + edge_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::make_pair(pos / vert, pos % vert); + } + + template + friend constexpr bool operator==(const edge_iterator &, const edge_iterator &) noexcept; + +private: + It it; + size_type vert; + size_type pos; + size_type last; + size_type offset{}; +}; + +template +[[nodiscard]] inline constexpr bool operator==(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return lhs.pos == rhs.pos; +} + +template +[[nodiscard]] inline constexpr bool operator!=(const edge_iterator &lhs, const edge_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic implementation of a directed adjacency matrix. + * @tparam Category Either a directed or undirected category tag. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class adjacency_matrix { + using alloc_traits = std::allocator_traits; + static_assert(std::is_base_of_v, "Invalid graph category"); + static_assert(std::is_same_v, "Invalid value type"); + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Vertex type. */ + using vertex_type = size_type; + /*! @brief Edge type. */ + using edge_type = std::pair; + /*! @brief Vertex iterator type. */ + using vertex_iterator = iota_iterator; + /*! @brief Edge iterator type. */ + using edge_iterator = internal::edge_iterator; + /*! @brief Out edge iterator type. */ + using out_edge_iterator = edge_iterator; + /*! @brief In edge iterator type. */ + using in_edge_iterator = edge_iterator; + /*! @brief Graph category tag. */ + using graph_category = Category; + + /*! @brief Default constructor. */ + adjacency_matrix() noexcept(noexcept(allocator_type{})) + : adjacency_matrix{0u} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit adjacency_matrix(const allocator_type &allocator) noexcept + : adjacency_matrix{0u, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied number of vertices. + * @param vertices Number of vertices. + * @param allocator The allocator to use. + */ + adjacency_matrix(const size_type vertices, const allocator_type &allocator = allocator_type{}) + : matrix{vertices * vertices, allocator}, + vert{vertices} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + adjacency_matrix(const adjacency_matrix &other) + : adjacency_matrix{other, other.get_allocator()} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + adjacency_matrix(const adjacency_matrix &other, const allocator_type &allocator) + : matrix{other.matrix, allocator}, + vert{other.vert} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + adjacency_matrix(adjacency_matrix &&other) noexcept + : adjacency_matrix{std::move(other), other.get_allocator()} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + adjacency_matrix(adjacency_matrix &&other, const allocator_type &allocator) + : matrix{std::move(other.matrix), allocator}, + vert{std::exchange(other.vert, 0u)} {} + + /** + * @brief Default copy assignment operator. + * @param other The instance to copy from. + * @return This container. + */ + adjacency_matrix &operator=(const adjacency_matrix &other) { + matrix = other.matrix; + vert = other.vert; + return *this; + } + + /** + * @brief Default move assignment operator. + * @param other The instance to move from. + * @return This container. + */ + adjacency_matrix &operator=(adjacency_matrix &&other) noexcept { + matrix = std::move(other.matrix); + vert = std::exchange(other.vert, 0u); + return *this; + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return matrix.get_allocator(); + } + + /*! @brief Clears the adjacency matrix. */ + void clear() noexcept { + matrix.clear(); + vert = {}; + } + + /** + * @brief Exchanges the contents with those of a given adjacency matrix. + * @param other Adjacency matrix to exchange the content with. + */ + void swap(adjacency_matrix &other) { + using std::swap; + swap(matrix, other.matrix); + swap(vert, other.vert); + } + + /** + * @brief Returns the number of vertices. + * @return The number of vertices. + */ + [[nodiscard]] size_type size() const noexcept { + return vert; + } + + /** + * @brief Returns an iterable object to visit all vertices of a matrix. + * @return An iterable object to visit all vertices of a matrix. + */ + [[nodiscard]] iterable_adaptor vertices() const noexcept { + return {0u, vert}; + } + + /** + * @brief Returns an iterable object to visit all edges of a matrix. + * @return An iterable object to visit all edges of a matrix. + */ + [[nodiscard]] iterable_adaptor edges() const noexcept { + const auto it = matrix.cbegin(); + const auto sz = matrix.size(); + return {{it, vert, 0u, sz, 1u}, {it, vert, sz, sz, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all out edges of a vertex. + * @param vertex The vertex of which to return all out edges. + * @return An iterable object to visit all out edges of a vertex. + */ + [[nodiscard]] iterable_adaptor out_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex * vert; + const auto to = from + vert; + return {{it, vert, from, to, 1u}, {it, vert, to, to, 1u}}; + } + + /** + * @brief Returns an iterable object to visit all in edges of a vertex. + * @param vertex The vertex of which to return all in edges. + * @return An iterable object to visit all in edges of a vertex. + */ + [[nodiscard]] iterable_adaptor in_edges(const vertex_type vertex) const noexcept { + const auto it = matrix.cbegin(); + const auto from = vertex; + const auto to = vert * vert + from; + return {{it, vert, from, to, vert}, {it, vert, to, to, vert}}; + } + + /** + * @brief Resizes an adjacency matrix. + * @param vertices The new number of vertices. + */ + void resize(const size_type vertices) { + adjacency_matrix other{vertices, get_allocator()}; + + for(auto [lhs, rhs]: edges()) { + other.insert(lhs, rhs); + } + + other.swap(*this); + } + + /** + * @brief Inserts an edge into the adjacency matrix, if it does not exist. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 1u; + } + + const auto inserted = !std::exchange(matrix[pos], 1u); + return {edge_iterator{matrix.cbegin(), vert, pos, matrix.size(), 1u}, inserted}; + } + + /** + * @brief Removes the edge associated with a pair of given vertices. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const vertex_type lhs, const vertex_type rhs) { + const auto pos = lhs * vert + rhs; + + if constexpr(std::is_same_v) { + const auto rev = rhs * vert + lhs; + ENTT_ASSERT(matrix[pos] == matrix[rev], "Something went really wrong"); + matrix[rev] = 0u; + } + + return std::exchange(matrix[pos], 0u); + } + + /** + * @brief Checks if an adjacency matrix contains a given edge. + * @param lhs The left hand vertex of the edge. + * @param rhs The right hand vertex of the edge. + * @return True if there is such an edge, false otherwise. + */ + [[nodiscard]] bool contains(const vertex_type lhs, const vertex_type rhs) const { + const auto pos = lhs * vert + rhs; + return pos < matrix.size() && matrix[pos]; + } + +private: + container_type matrix; + size_type vert; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Utility class for creating task graphs. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_flow { + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using task_container_type = dense_set, typename alloc_traits::template rebind_alloc>; + using ro_rw_container_type = std::vector, typename alloc_traits::template rebind_alloc>>; + using deps_container_type = dense_map, typename alloc_traits::template rebind_alloc>>; + using adjacency_matrix_type = adjacency_matrix>; + + void emplace(const id_type res, const bool is_rw) { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + + if(!deps.contains(res) && sync_on != vertices.size()) { + deps[res].emplace_back(sync_on, true); + } + + deps[res].emplace_back(index.first(), is_rw); + } + + void setup_graph(adjacency_matrix_type &matrix) const { + for(const auto &elem: deps) { + const auto last = elem.second.cend(); + auto it = elem.second.cbegin(); + + while(it != last) { + if(it->second) { + // rw item + if(auto curr = it++; it != last) { + if(it->second) { + matrix.insert(curr->first, it->first); + } else if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + matrix.insert(it->first, next->first); + } + } else { + for(; it != next; ++it) { + matrix.insert(curr->first, it->first); + } + } + } + } else { + // ro item (first iteration only) + if(const auto next = std::find_if(it, last, [](const auto &value) { return value.second; }); next != last) { + for(; it != next; ++it) { + matrix.insert(it->first, next->first); + } + } else { + it = last; + } + } + } + } + } + + void transitive_closure(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vk{}; vk < length; ++vk) { + for(std::size_t vi{}; vi < length; ++vi) { + for(std::size_t vj{}; vj < length; ++vj) { + if(matrix.contains(vi, vk) && matrix.contains(vk, vj)) { + matrix.insert(vi, vj); + } + } + } + } + } + + void transitive_reduction(adjacency_matrix_type &matrix) const { + const auto length = matrix.size(); + + for(std::size_t vert{}; vert < length; ++vert) { + matrix.erase(vert, vert); + } + + for(std::size_t vj{}; vj < length; ++vj) { + for(std::size_t vi{}; vi < length; ++vi) { + if(matrix.contains(vi, vj)) { + for(std::size_t vk{}; vk < length; ++vk) { + if(matrix.contains(vj, vk)) { + matrix.erase(vi, vk); + } + } + } + } + } + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Iterable task list. */ + using iterable = iterable_adaptor; + /*! @brief Adjacency matrix type. */ + using graph_type = adjacency_matrix_type; + + /*! @brief Default constructor. */ + basic_flow() + : basic_flow{allocator_type{}} {} + + /** + * @brief Constructs a flow builder with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_flow(const allocator_type &allocator) + : index{0u, allocator}, + vertices{allocator}, + deps{allocator}, + sync_on{} {} + + /*! @brief Default copy constructor. */ + basic_flow(const basic_flow &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + basic_flow(const basic_flow &other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{other.vertices, allocator}, + deps{other.deps, allocator}, + sync_on{other.sync_on} {} + + /*! @brief Default move constructor. */ + basic_flow(basic_flow &&) noexcept = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_flow(basic_flow &&other, const allocator_type &allocator) + : index{other.index.first(), allocator}, + vertices{std::move(other.vertices), allocator}, + deps{std::move(other.deps), allocator}, + sync_on{other.sync_on} {} + + /** + * @brief Default copy assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(const basic_flow &) = default; + + /** + * @brief Default move assignment operator. + * @return This flow builder. + */ + basic_flow &operator=(basic_flow &&) noexcept = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return allocator_type{index.second()}; + } + + /** + * @brief Returns the identifier at specified location. + * @param pos Position of the identifier to return. + * @return The requested identifier. + */ + [[nodiscard]] id_type operator[](const size_type pos) const { + return vertices.cbegin()[pos]; + } + + /*! @brief Clears the flow builder. */ + void clear() noexcept { + index.first() = {}; + vertices.clear(); + deps.clear(); + sync_on = {}; + } + + /** + * @brief Exchanges the contents with those of a given flow builder. + * @param other Flow builder to exchange the content with. + */ + void swap(basic_flow &other) { + using std::swap; + std::swap(index, other.index); + std::swap(vertices, other.vertices); + std::swap(deps, other.deps); + std::swap(sync_on, other.sync_on); + } + + /** + * @brief Returns the number of tasks. + * @return The number of tasks. + */ + [[nodiscard]] size_type size() const noexcept { + return vertices.size(); + } + + /** + * @brief Binds a task to a flow builder. + * @param value Task identifier. + * @return This flow builder. + */ + basic_flow &bind(const id_type value) { + sync_on += (sync_on == vertices.size()); + const auto it = vertices.emplace(value).first; + index.first() = size_type(it - vertices.begin()); + return *this; + } + + /** + * @brief Turns the current task into a sync point. + * @return This flow builder. + */ + basic_flow &sync() { + ENTT_ASSERT(index.first() < vertices.size(), "Invalid node"); + sync_on = index.first(); + + for(const auto &elem: deps) { + elem.second.emplace_back(sync_on, true); + } + + return *this; + } + + /** + * @brief Assigns a resource to the current task with a given access mode. + * @param res Resource identifier. + * @param is_rw Access mode. + * @return This flow builder. + */ + basic_flow &set(const id_type res, bool is_rw = false) { + emplace(res, is_rw); + return *this; + } + + /** + * @brief Assigns a read-only resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &ro(const id_type res) { + emplace(res, false); + return *this; + } + + /** + * @brief Assigns a range of read-only resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + ro(It first, It last) { + for(; first != last; ++first) { + emplace(*first, false); + } + + return *this; + } + + /** + * @brief Assigns a writable resource to the current task. + * @param res Resource identifier. + * @return This flow builder. + */ + basic_flow &rw(const id_type res) { + emplace(res, true); + return *this; + } + + /** + * @brief Assigns a range of writable resources to the current task. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return This flow builder. + */ + template + std::enable_if_t::value_type>, id_type>, basic_flow &> + rw(It first, It last) { + for(; first != last; ++first) { + emplace(*first, true); + } + + return *this; + } + + /** + * @brief Generates a task graph for the current content. + * @return The adjacency matrix of the task graph. + */ + [[nodiscard]] graph_type graph() const { + graph_type matrix{vertices.size(), get_allocator()}; + + setup_graph(matrix); + transitive_closure(matrix); + transitive_reduction(matrix); + + return matrix; + } + +private: + compressed_pair index; + task_container_type vertices; + deps_container_type deps; + size_type sync_on; +}; + +} // namespace entt + +#endif + +// #include "locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type 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 Type 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 Type 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 &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "meta/adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif + +// #include "meta/container.hpp" +#ifndef ENTT_META_CONTAINER_HPP +#define ENTT_META_CONTAINER_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../container/dense_set.hpp" +#ifndef ENTT_CONTAINER_DENSE_SET_HPP +#define ENTT_CONTAINER_DENSE_SET_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/memory.hpp" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +class dense_set_iterator final { + template + friend class dense_set_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::random_access_iterator_tag; + + constexpr dense_set_iterator() noexcept + : it{} {} + + constexpr dense_set_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_set_iterator(const dense_set_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_set_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_set_iterator operator++(int) noexcept { + dense_set_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_set_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_set_iterator operator--(int) noexcept { + dense_set_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_set_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_set_iterator operator+(const difference_type value) const noexcept { + dense_set_iterator copy = *this; + return (copy += value); + } + + constexpr dense_set_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_set_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return it[value].second; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it->second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_set_iterator &, const dense_set_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_set_iterator &, const dense_set_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_set_iterator &lhs, const dense_set_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_set_local_iterator final { + template + friend class dense_set_local_iterator; + +public: + using value_type = typename It::value_type::second_type; + using pointer = const value_type *; + using reference = const value_type &; + using difference_type = std::ptrdiff_t; + using iterator_category = std::forward_iterator_tag; + + constexpr dense_set_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_set_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_set_local_iterator(const dense_set_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_set_local_iterator &operator++() noexcept { + return offset = it[offset].first, *this; + } + + constexpr dense_set_local_iterator operator++(int) noexcept { + dense_set_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return std::addressof(it[offset].second); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return *operator->(); + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_set_local_iterator &lhs, const dense_set_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Associative container for unique objects of a given type. + * + * Internally, elements are organized into buckets. Which bucket an element is + * placed into depends entirely on its hash. Elements with the same hash code + * appear in the same bucket. + * + * @tparam Type Value type of the associative container. + * @tparam Hash Type of function to use to hash the values. + * @tparam KeyEqual Type of function to use to compare the values for equality. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class dense_set { + static constexpr float default_threshold = 0.875f; + static constexpr std::size_t minimum_capacity = 8u; + + using node_type = std::pair; + using alloc_traits = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t value_to_bucket(const Other &value) const noexcept { + return fast_mod(static_cast(sparse.second()(value)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &value, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(*it, value)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&value) { + const auto index = value_to_bucket(value); + + if(auto it = constrained_find(value, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + value_to_bucket(packed.first().back().second); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].first) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Type; + /*! @brief Value type of the container. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the elements. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the elements for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Random access iterator type. */ + using iterator = internal::dense_set_iterator; + /*! @brief Constant random access iterator type. */ + using const_iterator = internal::dense_set_iterator; + /*! @brief Reverse iterator type. */ + using reverse_iterator = std::reverse_iterator; + /*! @brief Constant reverse iterator type. */ + using const_reverse_iterator = std::reverse_iterator; + /*! @brief Forward iterator type. */ + using local_iterator = internal::dense_set_local_iterator; + /*! @brief Constant forward iterator type. */ + using const_local_iterator = internal::dense_set_local_iterator; + + /*! @brief Default constructor. */ + dense_set() + : dense_set{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_set(const allocator_type &allocator) + : dense_set{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const allocator_type &allocator) + : dense_set{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_set(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_set{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_set(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_set(const dense_set &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_set(const dense_set &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_set(dense_set &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_set(dense_set &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_set &operator=(const dense_set &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_set &operator=(dense_set &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Returns a reverse iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `rend()`. + * + * @return An iterator to the first instance of the reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crbegin() const noexcept { + return std::make_reverse_iterator(cend()); + } + + /*! @copydoc crbegin */ + [[nodiscard]] const_reverse_iterator rbegin() const noexcept { + return crbegin(); + } + + /*! @copydoc rbegin */ + [[nodiscard]] reverse_iterator rbegin() noexcept { + return std::make_reverse_iterator(end()); + } + + /** + * @brief Returns a reverse iterator to the end. + * @return An iterator to the element following the last instance of the + * reversed internal array. + */ + [[nodiscard]] const_reverse_iterator crend() const noexcept { + return std::make_reverse_iterator(cbegin()); + } + + /*! @copydoc crend */ + [[nodiscard]] const_reverse_iterator rend() const noexcept { + return crend(); + } + + /*! @copydoc rend */ + [[nodiscard]] reverse_iterator rend() noexcept { + return std::make_reverse_iterator(begin()); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if it does not exist. + * @param value An element to insert into the container. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value)); + } + + /** + * @brief Inserts elements into the container, if they do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Constructs an element in-place, if it does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace(Args &&...args) { + if constexpr(((sizeof...(Args) == 1u) && ... && std::is_same_v, value_type>)) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(std::piecewise_construct, std::make_tuple(packed.first().size()), std::forward_as_tuple(std::forward(args)...)); + const auto index = value_to_bucket(node.second); + + if(auto it = constrained_find(node.second, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.first, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(*pos); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].second); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given value. + * @param value Value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const value_type &value) { + for(size_type *curr = sparse.first().data() + value_to_bucket(value); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].first) { + if(packed.second()(packed.first()[*curr].second, value)) { + const auto index = *curr; + *curr = packed.first()[*curr].first; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_set &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Returns the number of elements matching a value (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const value_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given value. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const value_type &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const value_type &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Finds an element that compares _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return An iterator to an element with the given value. If no such + * element is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) { + return constrained_find(value, value_to_bucket(value)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &value) const { + return constrained_find(value, value_to_bucket(value)); + } + + /** + * @brief Returns a range containing all elements with a given value. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const value_type &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const value_type &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) { + const auto it = find(value); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &value) const { + const auto it = find(value); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given value. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const value_type &value) const { + return (find(value) != cend()); + } + + /** + * @brief Checks if the container contains an element that compares + * _equivalent_ to a given value. + * @tparam Other Type of an element to search for. + * @param value Value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &value) const { + return (find(value) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given element. + * @param value The value of the element to examine. + * @return The bucket for the given element. + */ + [[nodiscard]] size_type bucket(const value_type &value) const { + return value_to_bucket(value); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = value_to_bucket(packed.first()[pos].second); + packed.first()[pos].first = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the elements. + * @return The function used to hash the elements. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare elements for equality. + * @return The function used to compare elements for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +#endif + +// #include "context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + + +namespace entt { + +class meta_ctx; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map value{}; + + [[nodiscard]] inline static meta_context &from(meta_ctx &ctx); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::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.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_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 && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] 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> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "../core/utility.hpp" + +// #include "../locator/locator.hpp" +#ifndef ENTT_LOCATOR_LOCATOR_HPP +#define ENTT_LOCATOR_LOCATOR_HPP + +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + + +namespace entt { + +/** + * @brief Service locator, nothing more. + * + * A service locator is used to do what it promises: locate services.
+ * Usually service locators are tightly bound to the services they expose and + * thus it's hard to define a general purpose class to do that. This tiny class + * tries to fill the gap and to get rid of the burden of defining a different + * specific locator for each application. + * + * @note + * Users shouldn't retain references to a service. The recommended way is to + * retrieve the service implementation currently set each and every time the + * need for it arises. The risk is to incur in unexpected behaviors otherwise. + * + * @tparam Service Service type. + */ +template +class locator final { + class service_handle { + friend class locator; + std::shared_ptr value{}; + }; + +public: + /*! @brief Service type. */ + using type = Service; + /*! @brief Service node type. */ + using node_type = service_handle; + + /*! @brief Default constructor, deleted on purpose. */ + locator() = delete; + /*! @brief Default destructor, deleted on purpose. */ + ~locator() = delete; + + /** + * @brief Checks whether a service locator contains a value. + * @return True if the service locator contains a value, false otherwise. + */ + [[nodiscard]] static bool has_value() noexcept { + return (service != nullptr); + } + + /** + * @brief Returns a reference to a valid service, if any. + * + * @warning + * Invoking this function can result in undefined behavior if the service + * hasn't been set yet. + * + * @return A reference to the service currently set, if any. + */ + [[nodiscard]] static Service &value() noexcept { + ENTT_ASSERT(has_value(), "Service not available"); + return *service; + } + + /** + * @brief Returns a service if available or sets it from a fallback type. + * + * Arguments are used only if a service doesn't already exist. In all other + * cases, they are discarded. + * + * @tparam Args Types of arguments to use to construct the fallback service. + * @tparam Type 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 Type 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 Type 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 &emplace(std::allocator_arg_t, Allocator alloc, Args &&...args) { + service = std::allocate_shared(alloc, std::forward(args)...); + return *service; + } + + /** + * @brief Returns a handle to the underlying service. + * @return A handle to the underlying service. + */ + static node_type handle() noexcept { + node_type node{}; + node.value = service; + return node; + } + + /** + * @brief Resets or replaces a service. + * @param other Optional handle with which to replace the service. + */ + static void reset(const node_type &other = {}) noexcept { + service = other.value; + } + + /** + * @brief Resets or replaces a service. + * @tparam Type Service type. + * @tparam Deleter Deleter type. + * @param elem A pointer to a service to manage. + * @param deleter A deleter to use to destroy the service. + */ + template> + static void reset(Type *elem, Deleter deleter = {}) { + service = std::shared_ptr{elem, std::move(deleter)}; + } + +private: + // std::shared_ptr because of its type erased allocator which is useful here + inline static std::shared_ptr service{}; +}; + +} // namespace entt + +#endif + +// #include "adl_pointer.hpp" +#ifndef ENTT_META_ADL_POINTER_HPP +#define ENTT_META_ADL_POINTER_HPP + +namespace entt { + +/** + * @brief ADL based lookup function for dereferencing meta pointer-like types. + * @tparam Type Element type. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ +template +decltype(auto) dereference_meta_pointer_like(const Type &value) { + return *value; +} + +/** + * @brief Fake ADL based lookup function for meta pointer-like types. + * @tparam Type Element type. + */ +template +struct adl_meta_pointer_like { + /** + * @brief Uses the default ADL based lookup method to resolve the call. + * @param value A pointer-like object. + * @return The value returned from the dereferenced pointer. + */ + static decltype(auto) dereference(const Type &value) { + return dereference_meta_pointer_like(value); + } +}; + +} // namespace entt + +#endif + +// #include "context.hpp" + +// #include "fwd.hpp" +#ifndef ENTT_META_FWD_HPP +#define ENTT_META_FWD_HPP + +namespace entt { + +class meta_sequence_container; + +class meta_associative_container; + +class meta_any; + +struct meta_handle; + +struct meta_prop; + +struct meta_data; + +struct meta_func; + +class meta_type; + +} // namespace entt + +#endif + +// #include "node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "../core/enum.hpp" +#ifndef ENTT_CORE_ENUM_HPP +#define ENTT_CORE_ENUM_HPP + +#include + +namespace entt { + +/** + * @brief Enable bitmask support for enum classes. + * @tparam Type The enum type for which to enable bitmask support. + */ +template +struct enum_as_bitmask: std::false_type {}; + +/*! @copydoc enum_as_bitmask */ +template +struct enum_as_bitmask>: std::is_enum {}; + +/** + * @brief Helper variable template. + * @tparam Type The enum class type for which to enable bitmask support. + */ +template +inline constexpr bool enum_as_bitmask_v = enum_as_bitmask::value; + +} // namespace entt + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param lhs The first value to use. + * @param rhs The second value to use. + * @return The result of invoking the operator on the underlying types of the + * two values provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator|(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) | static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator&(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) & static_cast>(rhs)); +} + +/*! @copydoc operator| */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator^(const Type lhs, const Type rhs) noexcept { + return static_cast(static_cast>(lhs) ^ static_cast>(rhs)); +} + +/** + * @brief Operator available for enums for which bitmask support is enabled. + * @tparam Type Enum class type. + * @param value The value to use. + * @return The result of invoking the operator on the underlying types of the + * value provided. + */ +template +[[nodiscard]] constexpr std::enable_if_t, Type> +operator~(const Type value) noexcept { + return static_cast(~static_cast>(value)); +} + +/*! @copydoc operator~ */ +template +[[nodiscard]] constexpr std::enable_if_t, bool> +operator!(const Type value) noexcept { + return !static_cast>(value); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator|=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs | rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator&=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs & rhs)); +} + +/*! @copydoc operator| */ +template +constexpr std::enable_if_t, Type &> +operator^=(Type &lhs, const Type rhs) noexcept { + return (lhs = (lhs ^ rhs)); +} + +#endif + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#include + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +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 */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; + +template +auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) { + if(node.details) { + if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) { + return &it->second; + } + + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.second.type(context), id); elem) { + return elem; + } + } + } + + return static_cast*Member)>::mapped_type *>(nullptr); +} + +template +meta_type_node resolve(const meta_context &) noexcept; + +template +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + [[maybe_unused]] std::size_t pos{}; + meta_type_node (*value)(const meta_context &) noexcept = nullptr; + ((value = (pos++ == index ? &resolve>> : value)), ...); + ENTT_ASSERT(value != nullptr, "Out of bounds"); + return value(context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; + } + + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) { + if(from.info && *from.info == to) { + return func(instance, from); + } + + if(from.details) { + if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) { + return func(instance, it->second); + } + + for(auto &&curr: from.details->base) { + if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) { + return other; + } + } + } + + if(from.conversion_helper && arithmetic_or_enum) { + return func(instance, from.conversion_helper); + } + + return func(instance); +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct meta_range_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() noexcept + : meta_sequence_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to a sequence container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + value_type_node = &internal::resolve; + const_reference_node = &internal::resolve>>; + size_fn = meta_sequence_container_traits>::size; + clear_fn = meta_sequence_container_traits>::clear; + reserve_fn = meta_sequence_container_traits>::reserve; + resize_fn = meta_sequence_container_traits>::resize; + begin_fn = meta_sequence_container_traits>::begin; + end_fn = meta_sequence_container_traits>::end; + insert_fn = meta_sequence_container_traits>::insert; + erase_fn = meta_sequence_container_traits>::erase; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(const size_type); + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(iterator, meta_any); + inline iterator erase(iterator); + [[nodiscard]] inline meta_any operator[](const size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + const void *data{}; + bool const_only{}; +}; + +/*! @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() noexcept + : meta_associative_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to an associative container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + + size_fn = &meta_associative_container_traits>::size; + clear_fn = &meta_associative_container_traits>::clear; + reserve_fn = &meta_associative_container_traits>::reserve; + begin_fn = &meta_associative_container_traits>::begin; + end_fn = &meta_associative_container_traits>::end; + insert_fn = &meta_associative_container_traits>::insert; + erase_fn = &meta_associative_container_traits>::erase; + find_fn = &meta_associative_container_traits>::find; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Possible modes of a meta any object. */ +using meta_any_policy = any_policy; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *); + + template + static std::enable_if_t>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) { + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_meta_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(*static_cast(value)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(*static_cast(value))); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_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(*static_cast(value))); + } + } + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_sequence_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_associative_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + } + + void release() { + if(node.dtor.dtor && (storage.policy() == any_policy::owner)) { + node.dtor.dtor(storage.data()); + } + } + + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept + : storage{std::move(ref)}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept + : storage{}, + ctx{&area}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + release(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + release(); + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + release(); + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + [[nodiscard]] const Type *try_cast() const { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); + } + } + + /** + * @brief Tries to cast an instance to a given type. + * @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.policy() == any_policy::owner)) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return meta_any{meta_ctx_arg, *ctx}; + } else { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); + } + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + storage.emplace(std::forward(args)...); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + storage.reset(); + node = {}; + vtable = &basic_vtable; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (storage.policy() == any_policy::owner); + } + + /** + * @brief Returns the current mode of a meta any object. + * @return The current mode of the meta any object. + */ + [[nodiscard]] meta_any_policy policy() const noexcept { + return storage.policy(); + } + +private: + any storage; + const meta_ctx *ctx; + internal::meta_type_node node; + vtable_type *vtable; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance. + */ +struct meta_handle { + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the stored value by const reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /** + * @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_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_prop_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_data_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const noexcept; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_func_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; + } + } + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return node.id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); + } + + /** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr); + } + + /** + * @brief Checks if a type supports conversion it to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * + * In case of overloaded functions, a random one is returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param element A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc from_void */ + [[nodiscard]] meta_any from_void(const void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + if(node.details) { + if(auto it = node.details->func.find(id); it != node.details->func.cend()) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta properties (bases are also visited). + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key); + return elem ? meta_prop{*ctx, *elem} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); + } + +private: + internal::meta_type_node node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + if constexpr((std::is_same_v>, internal::meta_type_node> || ...)) { + return (args.from_void(*ctx, nullptr, instance), ...); + } else if constexpr((std::is_same_v>, internal::meta_conv_node> || ...)) { + return (args.conv(*ctx, instance), ...); + } else if constexpr((std::is_same_v>, decltype(internal::meta_type_node::conversion_helper)> || ...)) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + const auto value = (args(nullptr, instance), ...); + other.node.conversion_helper(other.data(), &value); + return other; + } else { + // forwards to force a compile-time error in case of available arguments + return meta_any{meta_ctx_arg, *ctx, std::forward(args)...}; + } + }); +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast({*ctx, node}); + return value && storage.assign(value.storage); +} + +inline bool meta_any::assign(meta_any &&other) { + if(*node.info == *other.node.info) { + return storage.assign(std::move(other.storage)); + } + + return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +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; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), 1, nullptr); + return *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + vtable(handle.data(), -1, nullptr); + return *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +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; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), nullptr); + return *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { + return size_fn(data); +} + +/** + * @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 !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @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) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it); + } + + return iterator{*ctx}; +} + +/** + * @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 const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { + return (mapped_type_node == nullptr); +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), std::as_const(key).data(), std::as_const(value).data()); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), std::as_const(key).data()) : 0u; +} + +/** + * @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 key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, std::as_const(key).data()) : iterator{*ctx}; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct fixed_size_sequence_container: std::true_type {}; + +template +struct fixed_size_sequence_container>: std::false_type {}; + +template +inline constexpr bool fixed_size_sequence_container_v = fixed_size_sequence_container::value; + +template +struct key_only_associative_container: std::true_type {}; + +template +struct key_only_associative_container>: std::false_type {}; + +template +inline constexpr bool key_only_associative_container_v = key_only_associative_container::value; + +template +struct reserve_aware_container: std::false_type {}; + +template +struct reserve_aware_container>: std::true_type {}; + +template +inline constexpr bool reserve_aware_container_v = reserve_aware_container::value; + +} // namespace internal +/*! @endcond */ + +/** + * @brief General purpose implementation of meta sequence container traits. + * @tparam Type Type of underlying sequence container. + */ +template +struct basic_meta_sequence_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool fixed_size = internal::fixed_size_sequence_container_v; + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_sequence_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_sequence_container::iterator; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear([[maybe_unused]] void *container) { + if constexpr(fixed_size) { + return false; + } else { + static_cast(container)->clear(); + return true; + } + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Resizes a container. + * @param container Opaque pointer to a container of the given type. + * @param sz The new number of elements. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool resize([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(fixed_size || !std::is_default_constructible_v) { + return false; + } else { + static_cast(container)->resize(sz); + return true; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast(container)->begin()} + : iterator{area, static_cast(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, static_cast(container)->end()} + : iterator{area, static_cast(as_const)->end()}; + } + + /** + * @brief Assigns one element to a container and constructs its object from + * a given opaque instance. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param value Optional opaque instance of the object to construct (as + * value type). + * @param cref Optional opaque instance of the object to construct (as + * decayed const reference type). + * @param it Iterator before which the element will be inserted. + * @return A possibly invalid iterator to the inserted element. + */ + [[nodiscard]] static iterator insert(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const void *value, [[maybe_unused]] const void *cref, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{area}; + } else { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->insert( + non_const ? *non_const : any_cast(it.base()), + value ? *static_cast(value) : *static_cast *>(cref))}; + } + } + + /** + * @brief Erases an element from a container. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param it An opaque iterator to the element to erase. + * @return A possibly invalid iterator following the last removed element. + */ + [[nodiscard]] static iterator erase(const meta_ctx &area, [[maybe_unused]] void *container, [[maybe_unused]] const iterator &it) { + if constexpr(fixed_size) { + return iterator{area}; + } else { + auto *const non_const = any_cast(&it.base()); + return {area, static_cast(container)->erase(non_const ? *non_const : any_cast(it.base()))}; + } + } +}; + +/** + * @brief General purpose implementation of meta associative container traits. + * @tparam Type Type of underlying associative container. + */ +template +struct basic_meta_associative_container_traits { + static_assert(std::is_same_v>>, "Unexpected type"); + + /*! @brief True in case of key-only containers, false otherwise. */ + static constexpr bool key_only = internal::key_only_associative_container_v; + + /*! @brief Unsigned integer type. */ + using size_type = typename meta_associative_container::size_type; + /*! @brief Meta iterator type. */ + using iterator = typename meta_associative_container::iterator; + + /** + * @brief Returns the number of elements in a container. + * @param container Opaque pointer to a container of the given type. + * @return Number of elements. + */ + [[nodiscard]] static size_type size(const void *container) { + return static_cast(container)->size(); + } + + /** + * @brief Clears a container. + * @param container Opaque pointer to a container of the given type. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool clear(void *container) { + static_cast(container)->clear(); + return true; + } + + /** + * @brief Increases the capacity of a container. + * @param container Opaque pointer to a container of the given type. + * @param sz Desired capacity. + * @return True in case of success, false otherwise. + */ + [[nodiscard]] static bool reserve([[maybe_unused]] void *container, [[maybe_unused]] const size_type sz) { + if constexpr(internal::reserve_aware_container_v) { + static_cast(container)->reserve(sz); + return true; + } else { + return false; + } + } + + /** + * @brief Returns a possibly const iterator to the beginning. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator to the first element of the container. + */ + static iterator begin(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->begin()} + : iterator{area, std::bool_constant{}, static_cast(as_const)->begin()}; + } + + /** + * @brief Returns a possibly const iterator to the end. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @return An iterator that is past the last element of the container. + */ + static iterator end(const meta_ctx &area, void *container, const void *as_const) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->end()} + : iterator{area, std::bool_constant{}, static_cast(as_const)->end()}; + } + + /** + * @brief Inserts an element into a container, if the key does not exist. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to insert. + * @param value Optional opaque value to insert (key-value containers). + * @return True if the insertion took place, false otherwise. + */ + [[nodiscard]] static bool insert(void *container, const void *key, [[maybe_unused]] const void *value) { + if constexpr(key_only) { + return static_cast(container)->insert(*static_cast(key)).second; + } else { + return static_cast(container)->emplace(*static_cast(key), *static_cast(value)).second; + } + } + + /** + * @brief Removes an element from a container. + * @param container Opaque pointer to a container of the given type. + * @param key An opaque key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + [[nodiscard]] static size_type erase(void *container, const void *key) { + return static_cast(container)->erase(*static_cast(key)); + } + + /** + * @brief Finds an element with a given key. + * @param area The context to pass to the newly created iterator. + * @param container Opaque pointer to a container of the given type. + * @param as_const Const opaque pointer fallback. + * @param key Opaque key value of an element to search for. + * @return An iterator to the element with the given key, if any. + */ + static iterator find(const meta_ctx &area, void *container, const void *as_const, const void *key) { + return container ? iterator{area, std::bool_constant{}, static_cast(container)->find(*static_cast(key))} + : iterator{area, std::bool_constant{}, static_cast(as_const)->find(*static_cast(key))}; + } +}; + +/** + * @brief Meta sequence container traits for `std::vector`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::array`s of any type. + * @tparam Type Template arguments for the container. + * @tparam N Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::list`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta sequence container traits for `std::deque`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_sequence_container_traits> + : basic_meta_sequence_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_map`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `std::unordered_set`s of any + * type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_map`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +/** + * @brief Meta associative container traits for `dense_set`s of any type. + * @tparam Args Template arguments for the container. + */ +template +struct meta_associative_container_traits> + : basic_meta_associative_container_traits> {}; + +} // namespace entt + +#endif + +// #include "meta/context.hpp" +#ifndef ENTT_META_CTX_HPP +#define ENTT_META_CTX_HPP + +// #include "../container/dense_map.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/utility.hpp" + + +namespace entt { + +class meta_ctx; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct meta_type_node; + +struct meta_context { + dense_map value{}; + + [[nodiscard]] inline static meta_context &from(meta_ctx &ctx); + [[nodiscard]] inline static const meta_context &from(const meta_ctx &ctx); +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Disambiguation tag for constructors and the like. */ +class meta_ctx_arg_t final {}; + +/*! @brief Constant of type meta_context_arg_t used to disambiguate calls. */ +inline constexpr meta_ctx_arg_t meta_ctx_arg{}; + +/*! @brief Opaque meta context type. */ +class meta_ctx: private internal::meta_context { + // attorney idiom like model to access the base class + friend struct internal::meta_context; +}; + +/*! @cond TURN_OFF_DOXYGEN */ +[[nodiscard]] inline internal::meta_context &internal::meta_context::from(meta_ctx &ctx) { + return ctx; +} + +[[nodiscard]] inline const internal::meta_context &internal::meta_context::from(const meta_ctx &ctx) { + return ctx; +} +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta/factory.hpp" +#ifndef ENTT_META_FACTORY_HPP +#define ENTT_META_FACTORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "range.hpp" + +// #include "resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || 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 + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +using meta_function_helper_t = typename meta_function_helper::type; + +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + return true; + } + } + } + } + + return false; +} + +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { // NOLINT + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{meta_ctx_arg, ctx}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +[[nodiscard]] inline decltype(auto) owner(meta_ctx &ctx, const type_info &info) { + auto &&context = internal::meta_context::from(ctx); + ENTT_ASSERT(context.value.contains(info.hash()), "Type not available"); + return context.value[info.hash()]; +} + +inline meta_data_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_data_node node) { + return parent.details->data.insert_or_assign(id, std::move(node)).first->second; +} + +inline meta_func_node &meta_extend(internal::meta_type_node &parent, const id_type id, meta_func_node node) { + if(auto it = parent.details->func.find(id); it != parent.details->func.end()) { + for(auto *curr = &it->second; curr; curr = curr->next.get()) { + if(curr->invoke == node.invoke) { + node.next = std::move(curr->next); + *curr = std::move(node); + return *curr; + } + } + + // locally overloaded function + node.next = std::make_shared(std::move(parent.details->func[id])); + } + + return parent.details->func.insert_or_assign(id, std::move(node)).first->second; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic meta factory to be used for reflection purposes. + * @tparam Type Reflected type for which the factory was created. + */ +template +class meta_factory { + template + void data(const id_type id, std::index_sequence) noexcept { + using data_type = std::invoke_result_t; + using args_type = type_list)>::args_type...>; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + (std::is_member_object_pointer_v)> && ... && std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none, + Setter::size, + &internal::resolve>>, + &meta_arg::size != 1u, type_list_element_t>...>>, + +[](meta_handle instance, meta_any value) { return (meta_setter>(*instance.operator->(), value.as_ref()) || ...); }, + &meta_getter}); + + bucket = &elem.prop; + } + +public: + /*! @brief Default constructor. */ + meta_factory() noexcept + : meta_factory{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context into which to construct meta types. + */ + meta_factory(meta_ctx &area) noexcept + : ctx{&area}, + bucket{}, + info{&type_id()} { + auto &&elem = internal::owner(*ctx, *info); + + if(!elem.details) { + elem.details = std::make_shared(); + } + + bucket = &elem.details->prop; + } + + /** + * @brief Assigns a custom unique identifier to a meta type. + * @param id A custom unique identifier. + * @return A meta factory for the given type. + */ + auto type(const id_type id) noexcept { + auto &&elem = internal::owner(*ctx, *info); + ENTT_ASSERT(elem.id == id || !resolve(*ctx, id), "Duplicate identifier"); + bucket = &elem.details->prop; + elem.id = id; + return *this; + } + + /** + * @brief Assigns a meta base to a meta type. + * + * A reflected base class must be a real base class of the reflected type. + * + * @tparam Base Type of the base class to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto base() noexcept { + static_assert(!std::is_same_v && std::is_base_of_v, "Invalid base type"); + auto *const op = +[](const void *instance) noexcept { return static_cast(static_cast(static_cast(instance))); }; + internal::owner(*ctx, *info).details->base.insert_or_assign(type_id().hash(), internal::meta_base_node{&internal::resolve, op}); + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * Conversion functions can be either free functions or member + * functions.
+ * In case of free functions, they must accept a const reference to an + * instance of the parent type as an argument. In case of member functions, + * they should have no arguments at all. + * + * @tparam Candidate The actual function to use for the conversion. + * @return A meta factory for the parent type. + */ + template + auto conv() noexcept { + using conv_type = std::remove_cv_t>>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, std::invoke(Candidate, *static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta conversion function to a meta type. + * + * The given type must be such that an instance of the reflected type can be + * converted to it. + * + * @tparam To Type of the conversion function to assign to the meta type. + * @return A meta factory for the parent type. + */ + template + auto conv() noexcept { + using conv_type = std::remove_cv_t>; + auto *const op = +[](const meta_ctx &area, const void *instance) { return forward_as_meta(area, static_cast(*static_cast(instance))); }; + internal::owner(*ctx, *info).details->conv.insert_or_assign(type_id().hash(), internal::meta_conv_node{op}); + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * Both member functions and free function can be assigned to meta types in + * the role of constructors. All that is required is that they return an + * instance of the underlying type.
+ * From a client's point of view, nothing changes if a constructor of a meta + * type is a built-in one or not. + * + * @tparam Candidate The actual function to use as a constructor. + * @tparam Policy Optional policy (no policy set by default). + * @return A meta factory for the parent type. + */ + template + auto ctor() noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + static_assert(std::is_same_v>, Type>, "The function doesn't return an object of the required type"); + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta constructor to a meta type. + * + * A meta constructor is uniquely identified by the types of its arguments + * and is such that there exists an actual constructor of the underlying + * type that can be invoked with parameters whose types are those given. + * + * @tparam Args Types of arguments to use to construct an instance. + * @return A meta factory for the parent type. + */ + template + auto ctor() noexcept { + // default constructor is already implicitly generated, no need for redundancy + if constexpr(sizeof...(Args) != 0u) { + using descriptor = meta_function_helper_t; + internal::owner(*ctx, *info).details->ctor.insert_or_assign(type_id().hash(), internal::meta_ctor_node{descriptor::args_type::size, &meta_arg, &meta_construct}); + } + + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta destructor to a meta type. + * + * Both free functions and member functions can be assigned to meta types in + * the role of destructors.
+ * The signature of a free function should be identical to the following: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * Member functions should not take arguments instead.
+ * The purpose is to give users the ability to free up resources that + * require special treatment before an object is actually destroyed. + * + * @tparam Func The actual function to use as a destructor. + * @return A meta factory for the parent type. + */ + template + auto dtor() noexcept { + static_assert(std::is_invocable_v, "The function doesn't accept an object of the type provided"); + auto *const op = +[](void *instance) { std::invoke(Func, *static_cast(instance)); }; + internal::owner(*ctx, *info).dtor = internal::meta_dtor_node{op}; + bucket = nullptr; + return *this; + } + + /** + * @brief Assigns a meta data to a meta type. + * + * Both data members and static and global variables, as well as constants + * of any kind, can be assigned to a meta type.
+ * From a client's point of view, all the variables associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Data The actual variable to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + if constexpr(std::is_member_object_pointer_v) { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + std::is_const_v> ? internal::meta_traits::is_const : internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } else { + using data_type = std::remove_pointer_t; + + if constexpr(std::is_pointer_v) { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } else { + static_assert(Policy::template value, "Invalid return type for the given policy"); + } + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + ((std::is_same_v>> || std::is_const_v>) ? internal::meta_traits::is_const : internal::meta_traits::is_none) | internal::meta_traits::is_static, + 1u, + &internal::resolve>>, + &meta_arg>>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setter and + * getter. + * + * Setters and getters can be either free functions, member functions or a + * mix of them.
+ * In case of free functions, setters and getters must accept a reference to + * an instance of the parent type as their first argument. A setter has then + * an extra argument of a type convertible to that of the parameter to + * set.
+ * In case of member functions, getters have no arguments at all, while + * setters has an argument of a type convertible to that of the parameter to + * set. + * + * @tparam Setter The actual function to use as a setter. + * @tparam Getter The actual function to use as a getter. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + using data_type = std::invoke_result_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + if constexpr(std::is_same_v) { + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static */ + internal::meta_traits::is_const, + 0u, + &internal::resolve>>, + &meta_arg>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } else { + using args_type = typename meta_function_helper_t::args_type; + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_data_node{ + /* this is never static nor const */ + internal::meta_traits::is_none, + 1u, + &internal::resolve>>, + &meta_arg>>, + &meta_setter, + &meta_getter}); + + bucket = &elem.prop; + } + + return *this; + } + + /** + * @brief Assigns a meta data to a meta type by means of its setters and + * getter. + * + * Multi-setter support for meta data members. All setters are tried in the + * order of definition before returning to the caller.
+ * Setters can be either free functions, member functions or a mix of them + * and are provided via a `value_list` type. + * + * @sa data + * + * @tparam Setter The actual functions to use as setters. + * @tparam Getter The actual getter function. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template + auto data(const id_type id) noexcept { + data(id, std::make_index_sequence{}); + return *this; + } + + /** + * @brief Assigns a meta function to a meta type. + * + * Both member functions and free functions can be assigned to a meta + * type.
+ * From a client's point of view, all the functions associated with the + * reflected object will appear as if they were part of the type itself. + * + * @tparam Candidate The actual function to attach to the meta type. + * @tparam Policy Optional policy (no policy set by default). + * @param id Unique identifier. + * @return A meta factory for the parent type. + */ + template + auto func(const id_type id) noexcept { + using descriptor = meta_function_helper_t; + static_assert(Policy::template value, "Invalid return type for the given policy"); + + auto &&elem = internal::meta_extend( + internal::owner(*ctx, *info), + id, + internal::meta_func_node{ + (descriptor::is_const ? internal::meta_traits::is_const : internal::meta_traits::is_none) | (descriptor::is_static ? internal::meta_traits::is_static : internal::meta_traits::is_none), + descriptor::args_type::size, + &internal::resolve, void, std::remove_cv_t>>>, + &meta_arg, + &meta_invoke}); + + bucket = &elem.prop; + return *this; + } + + /** + * @brief Assigns a property to the last meta object created. + * + * Both the key and the value (if any) must be at least copy constructible. + * + * @tparam Value Optional type of the property value. + * @param id Property key. + * @param value Optional property value. + * @return A meta factory for the parent type. + */ + template + meta_factory prop(id_type id, [[maybe_unused]] Value &&...value) { + ENTT_ASSERT(bucket != nullptr, "Meta object does not support properties"); + + if constexpr(sizeof...(Value) == 0u) { + (*bucket)[id] = internal::meta_prop_node{&internal::resolve}; + } else { + (*bucket)[id] = internal::meta_prop_node{ + &internal::resolve>..., + std::make_shared>(std::forward(value))...}; + } + + return *this; + } + +private: + meta_ctx *ctx; + dense_map *bucket; + const type_info *info; +}; + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @param ctx The context into which to construct meta types. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta(meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + // make sure the type exists in the context before returning a factory + context.value.try_emplace(type_id().hash(), internal::resolve(context)); + return meta_factory{ctx}; +} + +/** + * @brief Utility function to use for reflection. + * + * This is the point from which everything starts.
+ * By invoking this function with a type that is not yet reflected, a meta type + * is created to which it will be possible to attach meta objects through a + * dedicated factory. + * + * @tparam Type Type to reflect. + * @return A meta factory for the given type. + */ +template +[[nodiscard]] auto meta() noexcept { + return meta(locator::value_or()); +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx, const id_type id) noexcept { + auto &&context = internal::meta_context::from(ctx); + + for(auto it = context.value.begin(); it != context.value.end();) { + if(it->second.id == id) { + it = context.value.erase(it); + } else { + ++it; + } + } +} + +/** + * @brief Resets a type and all its parts. + * + * Resets a type and all its data members, member functions and properties, as + * well as its constructors, destructors and conversion functions if any.
+ * Base classes aren't reset but the link between the two types is removed. + * + * The type is also removed from the set of searchable types. + * + * @param id Unique identifier. + */ +inline void meta_reset(const id_type id) noexcept { + meta_reset(locator::value_or(), id); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + * @param ctx The context from which to reset meta types. + */ +template +void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.erase(type_id().hash()); +} + +/** + * @brief Resets a type and all its parts. + * + * @sa meta_reset + * + * @tparam Type Type to reset. + */ +template +void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + * + * @param ctx The context from which to reset meta types. + */ +inline void meta_reset(meta_ctx &ctx) noexcept { + internal::meta_context::from(ctx).value.clear(); +} + +/** + * @brief Resets all meta types. + * + * @sa meta_reset + */ +inline void meta_reset() noexcept { + meta_reset(locator::value_or()); +} + +} // namespace entt + +#endif + +// #include "meta/meta.hpp" +#ifndef ENTT_META_META_HPP +#define ENTT_META_META_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/any.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "../locator/locator.hpp" + +// #include "adl_pointer.hpp" + +// #include "context.hpp" + +// #include "fwd.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; + +/*! @brief Proxy object for sequence containers. */ +class meta_sequence_container { + class meta_iterator; + +public: + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Meta iterator type. */ + using iterator = meta_iterator; + + /*! @brief Default constructor. */ + meta_sequence_container() noexcept + : meta_sequence_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_sequence_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to a sequence container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + value_type_node = &internal::resolve; + const_reference_node = &internal::resolve>>; + size_fn = meta_sequence_container_traits>::size; + clear_fn = meta_sequence_container_traits>::clear; + reserve_fn = meta_sequence_container_traits>::reserve; + resize_fn = meta_sequence_container_traits>::resize; + begin_fn = meta_sequence_container_traits>::begin; + end_fn = meta_sequence_container_traits>::end; + insert_fn = meta_sequence_container_traits>::insert; + erase_fn = meta_sequence_container_traits>::erase; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool resize(const size_type); + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline iterator insert(iterator, meta_any); + inline iterator erase(iterator); + [[nodiscard]] inline meta_any operator[](const size_type); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*const_reference_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + bool (*resize_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + iterator (*insert_fn)(const meta_ctx &, void *, const void *, const void *, const iterator &){}; + iterator (*erase_fn)(const meta_ctx &, void *, const iterator &){}; + const void *data{}; + bool const_only{}; +}; + +/*! @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() noexcept + : meta_associative_container{locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_associative_container(const meta_ctx &area) noexcept + : ctx{&area} {} + + /** + * @brief Rebinds a proxy object to an associative container type. + * @tparam Type Type of container to wrap. + * @param instance The container to wrap. + */ + template + void rebind(Type &instance) noexcept { + key_type_node = &internal::resolve; + value_type_node = &internal::resolve; + + if constexpr(!meta_associative_container_traits>::key_only) { + mapped_type_node = &internal::resolve; + } + + size_fn = &meta_associative_container_traits>::size; + clear_fn = &meta_associative_container_traits>::clear; + reserve_fn = &meta_associative_container_traits>::reserve; + begin_fn = &meta_associative_container_traits>::begin; + end_fn = &meta_associative_container_traits>::end; + insert_fn = &meta_associative_container_traits>::insert; + erase_fn = &meta_associative_container_traits>::erase; + find_fn = &meta_associative_container_traits>::find; + const_only = std::is_const_v; + data = &instance; + } + + [[nodiscard]] inline bool key_only() const noexcept; + [[nodiscard]] inline meta_type key_type() const noexcept; + [[nodiscard]] inline meta_type mapped_type() const noexcept; + [[nodiscard]] inline meta_type value_type() const noexcept; + [[nodiscard]] inline size_type size() const noexcept; + inline bool clear(); + inline bool reserve(const size_type); + [[nodiscard]] inline iterator begin(); + [[nodiscard]] inline iterator end(); + inline bool insert(meta_any, meta_any); + inline size_type erase(meta_any); + [[nodiscard]] inline iterator find(meta_any); + [[nodiscard]] inline explicit operator bool() const noexcept; + +private: + const meta_ctx *ctx{}; + internal::meta_type_node (*key_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*mapped_type_node)(const internal::meta_context &){}; + internal::meta_type_node (*value_type_node)(const internal::meta_context &){}; + size_type (*size_fn)(const void *){}; + bool (*clear_fn)(void *){}; + bool (*reserve_fn)(void *, const size_type){}; + iterator (*begin_fn)(const meta_ctx &, void *, const void *){}; + iterator (*end_fn)(const meta_ctx &, void *, const void *){}; + bool (*insert_fn)(void *, const void *, const void *){}; + size_type (*erase_fn)(void *, const void *){}; + iterator (*find_fn)(const meta_ctx &, void *, const void *, const void *){}; + const void *data{}; + bool const_only{}; +}; + +/*! @brief Possible modes of a meta any object. */ +using meta_any_policy = any_policy; + +/*! @brief Opaque wrapper for values of any type. */ +class meta_any { + using vtable_type = void(const internal::meta_traits op, const bool, const void *, void *); + + template + static std::enable_if_t>, Type>> basic_vtable([[maybe_unused]] const internal::meta_traits req, [[maybe_unused]] const bool const_only, [[maybe_unused]] const void *value, [[maybe_unused]] void *other) { + if constexpr(is_meta_pointer_like_v) { + if(req == internal::meta_traits::is_meta_pointer_like) { + if constexpr(std::is_function_v::element_type>) { + static_cast(other)->emplace(*static_cast(value)); + } else if constexpr(!std::is_void_v::element_type>>) { + using in_place_type = decltype(adl_meta_pointer_like::dereference(*static_cast(value))); + + if constexpr(std::is_constructible_v) { + if(const auto &pointer_like = *static_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(*static_cast(value))); + } + } + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_sequence_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + + if constexpr(is_complete_v>) { + if(req == internal::meta_traits::is_meta_associative_container) { + const_only ? static_cast(other)->rebind(*static_cast(value)) : static_cast(other)->rebind(*static_cast(const_cast(value))); + } + } + } + + void release() { + if(node.dtor.dtor && (storage.policy() == any_policy::owner)) { + node.dtor.dtor(storage.data()); + } + } + + meta_any(const meta_ctx &area, const meta_any &other, any ref) noexcept + : storage{std::move(ref)}, + ctx{&area}, + node{storage ? other.node : internal::meta_type_node{}}, + vtable{storage ? other.vtable : &basic_vtable} {} + +public: + /*! Default constructor. */ + meta_any() noexcept + : meta_any{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_any(meta_ctx_arg_t, const meta_ctx &area) noexcept + : storage{}, + ctx{&area}, + node{}, + vtable{&basic_vtable} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(std::in_place_type_t, Args &&...args) + : meta_any{locator::value_or(), std::in_place_type, std::forward(args)...} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param area The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + */ + template + explicit meta_any(const meta_ctx &area, std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + ctx{&area}, + node{internal::resolve>>(internal::meta_context::from(*ctx))}, + vtable{&basic_vtable>>} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(Type &&value) + : meta_any{locator::value_or(), std::forward(value)} {} + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param area The context from which to search for meta types. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, meta_any>>> + meta_any(const meta_ctx &area, Type &&value) + : meta_any{area, std::in_place_type>, std::forward(value)} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_any(const meta_ctx &area, const meta_any &other) + : meta_any{other} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_any(const meta_ctx &area, meta_any &&other) + : meta_any{std::move(other)} { + ctx = &area; + node = node.resolve ? node.resolve(internal::meta_context::from(*ctx)) : node; + } + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + meta_any(const meta_any &other) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + meta_any(meta_any &&other) noexcept + : storage{std::move(other.storage)}, + ctx{other.ctx}, + node{std::exchange(other.node, internal::meta_type_node{})}, + vtable{std::exchange(other.vtable, &basic_vtable)} {} + + /*! @brief Frees the internal storage, whatever it means. */ + ~meta_any() { + release(); + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This meta any object. + */ + meta_any &operator=(const meta_any &other) { + release(); + storage = other.storage; + ctx = other.ctx; + node = other.node; + vtable = other.vtable; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This meta any object. + */ + meta_any &operator=(meta_any &&other) noexcept { + release(); + storage = std::move(other.storage); + ctx = other.ctx; + node = std::exchange(other.node, internal::meta_type_node{}); + vtable = std::exchange(other.vtable, &basic_vtable); + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This meta any object. + */ + template + std::enable_if_t, meta_any>, meta_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /*! @copydoc any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /*! @copydoc any::data */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc any::data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Invokes the underlying function, if possible. + * @tparam Args Types of arguments to use to invoke the function. + * @param id Unique identifier. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, Args &&...args) const; + + /*! @copydoc invoke */ + template + meta_any invoke(const id_type id, Args &&...args); + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, Type &&value); + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id) const; + + /*! @copydoc get */ + [[nodiscard]] meta_any get(const id_type id); + + /** + * @brief Tries to cast an instance to a given type. + * @tparam Type Type to which to cast the instance. + * @return A (possibly null) pointer to the contained instance. + */ + template + [[nodiscard]] const Type *try_cast() const { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data())); + } + + /*! @copydoc try_cast */ + template + [[nodiscard]] Type *try_cast() { + if constexpr(std::is_const_v) { + return std::as_const(*this).try_cast>(); + } else { + const auto other = internal::resolve>(internal::meta_context::from(*ctx)); + return static_cast(const_cast(internal::try_cast(internal::meta_context::from(*ctx), node, other, data()))); + } + } + + /** + * @brief Tries to cast an instance to a given type. + * @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.policy() == any_policy::owner)) { + std::swap(*this, other); + } + + return true; + } + + return false; + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return A valid meta any object if there exists a viable conversion, an + * invalid one otherwise. + */ + template + [[nodiscard]] meta_any allow_cast() const { + if constexpr(std::is_reference_v && !std::is_const_v>) { + return meta_any{meta_ctx_arg, *ctx}; + } else { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}); + } + } + + /** + * @brief Converts an object in such a way that a given cast becomes viable. + * @tparam Type Type to which the cast is requested. + * @return True if there exists a viable conversion, false otherwise. + */ + template + [[nodiscard]] bool allow_cast() { + auto other = internal::resolve>>(internal::meta_context::from(*ctx)); + return allow_cast(meta_type{*ctx, other}) && (!(std::is_reference_v && !std::is_const_v>) || storage.data() != nullptr); + } + + /*! @copydoc any::emplace */ + template + void emplace(Args &&...args) { + release(); + storage.emplace(std::forward(args)...); + node = internal::resolve>>(internal::meta_context::from(*ctx)); + vtable = &basic_vtable>>; + } + + /*! @copydoc any::assign */ + bool assign(const meta_any &other); + + /*! @copydoc any::assign */ + bool assign(meta_any &&other); + + /*! @copydoc any::reset */ + void reset() { + release(); + storage.reset(); + node = {}; + vtable = &basic_vtable; + } + + /** + * @brief Returns a sequence container proxy. + * @return A sequence container proxy for the underlying object. + */ + [[nodiscard]] meta_sequence_container as_sequence_container() noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_sequence_container */ + [[nodiscard]] meta_sequence_container as_sequence_container() const noexcept { + meta_sequence_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_sequence_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Returns an associative container proxy. + * @return An associative container proxy for the underlying object. + */ + [[nodiscard]] meta_associative_container as_associative_container() noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, policy() == meta_any_policy::cref, std::as_const(*this).data(), &proxy); + return proxy; + } + + /*! @copydoc as_associative_container */ + [[nodiscard]] meta_associative_container as_associative_container() const noexcept { + meta_associative_container proxy{*ctx}; + vtable(internal::meta_traits::is_meta_associative_container, true, data(), &proxy); + return proxy; + } + + /** + * @brief Indirection operator for dereferencing opaque objects. + * @return A wrapper that shares a reference to an unmanaged object if the + * wrapped element is dereferenceable, an invalid meta any otherwise. + */ + [[nodiscard]] meta_any operator*() const noexcept { + meta_any ret{meta_ctx_arg, *ctx}; + vtable(internal::meta_traits::is_meta_pointer_like, true, storage.data(), &ret); + return ret; + } + + /** + * @brief Returns false if a wrapper is invalid, true otherwise. + * @return False if the wrapper is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(node.info == nullptr); + } + + /*! @copydoc any::operator== */ + [[nodiscard]] bool operator==(const meta_any &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info && storage == other.storage)); + } + + /*! @copydoc any::operator!= */ + [[nodiscard]] bool operator!=(const meta_any &other) const noexcept { + return !(*this == other); + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::as_ref */ + [[nodiscard]] meta_any as_ref() const noexcept { + return meta_any{*ctx, *this, storage.as_ref()}; + } + + /*! @copydoc any::owner */ + [[deprecated("use policy() and meta_any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (storage.policy() == any_policy::owner); + } + + /** + * @brief Returns the current mode of a meta any object. + * @return The current mode of the meta any object. + */ + [[nodiscard]] meta_any_policy policy() const noexcept { + return storage.policy(); + } + +private: + any storage; + const meta_ctx *ctx; + internal::meta_type_node node; + vtable_type *vtable; +}; + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @param ctx The context from which to search for meta types. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(const meta_ctx &ctx, Type &&value) { + return meta_any{ctx, std::in_place_type, std::forward(value)}; +} + +/** + * @brief Forwards its argument and avoids copies for lvalue references. + * @tparam Type Type of argument to use to construct the new instance. + * @param value Parameter to use to construct the instance. + * @return A properly initialized and not necessarily owning wrapper. + */ +template +[[nodiscard]] meta_any forward_as_meta(Type &&value) { + return forward_as_meta(locator::value_or(), std::forward(value)); +} + +/** + * @brief Opaque pointers to instances of any type. + * + * A handle doesn't perform copies and isn't responsible for the contained + * object. It doesn't prolong the lifetime of the pointed instance. + */ +struct meta_handle { + /*! Default constructor. */ + meta_handle() noexcept + : meta_handle{meta_ctx_arg, locator::value_or()} {} + + /** + * @brief Context aware constructor. + * @param area The context from which to search for meta types. + */ + meta_handle(meta_ctx_arg_t, const meta_ctx &area) noexcept + : any{meta_ctx_arg, area} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @param value An instance of an object to use to initialize the handle. + */ + meta_handle(const meta_any &value) noexcept + : any{value.as_ref()} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param ctx The context from which to search for meta types. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(const meta_ctx &ctx, Type &value) noexcept + : any{ctx, std::in_place_type, value} {} + + /** + * @brief Creates a handle that points to an unmanaged object. + * @tparam Type Type of object to use to initialize the handle. + * @param value An instance of an object to use to initialize the handle. + */ + template, meta_handle>>> + meta_handle(Type &value) noexcept + : meta_handle{locator::value_or(), value} {} + + /** + * @brief Context aware copy constructor. + * @param area The context from which to search for meta types. + * @param other The instance to copy from. + */ + meta_handle(const meta_ctx &area, const meta_handle &other) + : any{area, other.any} {} + + /** + * @brief Context aware move constructor. + * @param area The context from which to search for meta types. + * @param other The instance to move from. + */ + meta_handle(const meta_ctx &area, meta_handle &&other) + : any{area, std::move(other.any)} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + meta_handle(const meta_handle &) = delete; + + /*! @brief Default move constructor. */ + meta_handle(meta_handle &&) = default; + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This meta handle. + */ + meta_handle &operator=(const meta_handle &) = delete; + + /** + * @brief Default move assignment operator. + * @return This meta handle. + */ + meta_handle &operator=(meta_handle &&) = default; + + /** + * @brief Returns false if a handle is invalid, true otherwise. + * @return False if the handle is invalid, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(any); + } + + /*! @copydoc meta_any::operator== */ + [[nodiscard]] bool operator==(const meta_handle &other) const noexcept { + return (any == other.any); + } + + /*! @copydoc meta_any::operator!= */ + [[nodiscard]] bool operator!=(const meta_handle &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Access operator for accessing the contained opaque object. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] meta_any *operator->() { + return &any; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const meta_any *operator->() const { + return &any; + } + +private: + meta_any any; +}; + +/*! @brief Opaque wrapper for properties of any type. */ +struct meta_prop { + /*! @brief Default constructor. */ + meta_prop() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_prop(const meta_ctx &area, const internal::meta_prop_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the stored value by const reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() const { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, nullptr, node->value.get()) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns the stored value by reference. + * @return A wrapper containing the value stored with the property. + */ + [[nodiscard]] meta_any value() { + return node->value ? node->type(internal::meta_context::from(*ctx)).from_void(*ctx, node->value.get(), nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /** + * @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_prop &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_prop_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_prop &lhs, const meta_prop &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for data members. */ +struct meta_data { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_data_node::size_type; + + /*! @brief Default constructor. */ + meta_data() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_data(const meta_ctx &area, const internal::meta_data_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of setters available. + * @return The number of setters available. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a data member is constant or not. + * @return True if the data member is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a data member is static or not. + * @return True if the data member is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /*! @copydoc meta_any::type */ + [[nodiscard]] inline meta_type type() const noexcept; + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(meta_handle instance, Type &&value) const { + return node->set && node->set(meta_handle{*ctx, std::move(instance)}, meta_any{*ctx, std::forward(value)}); + } + + /** + * @brief Gets the value of a given variable. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(meta_handle instance) const { + return node->get(*ctx, meta_handle{*ctx, std::move(instance)}); + } + + /** + * @brief Returns the type accepted by the i-th setter. + * @param index Index of the setter of which to return the accepted type. + * @return The type accepted by the i-th setter. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Returns a range to visit registered meta properties. + * @return An iterable range to visit registered meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_data &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_data_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_data &lhs, const meta_data &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for member functions. */ +struct meta_func { + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_func_node::size_type; + + /*! @brief Default constructor. */ + meta_func() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_func(const meta_ctx &area, const internal::meta_func_node &curr) noexcept + : node{&curr}, + ctx{&area} {} + + /** + * @brief Returns the number of arguments accepted by a member function. + * @return The number of arguments accepted by the member function. + */ + [[nodiscard]] size_type arity() const noexcept { + return node->arity; + } + + /** + * @brief Indicates whether a member function is constant or not. + * @return True if the member function is constant, false otherwise. + */ + [[nodiscard]] bool is_const() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_const); + } + + /** + * @brief Indicates whether a member function is static or not. + * @return True if the member function is static, false otherwise. + */ + [[nodiscard]] bool is_static() const noexcept { + return static_cast(node->traits & internal::meta_traits::is_static); + } + + /** + * @brief Returns the return type of a member function. + * @return The return type of the member function. + */ + [[nodiscard]] inline meta_type ret() const noexcept; + + /** + * @brief Returns the type of the i-th argument of a member function. + * @param index Index of the argument of which to return the type. + * @return The type of the i-th argument of a member function. + */ + [[nodiscard]] inline meta_type arg(const size_type index) const noexcept; + + /** + * @brief Invokes the underlying function, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(meta_handle instance, meta_any *const args, const size_type sz) const { + return sz == arity() ? node->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(std::move(instance), arguments, sizeof...(Args)); + } + + /*! @copydoc meta_data::prop */ + [[nodiscard]] meta_range prop() const noexcept { + return {{*ctx, node->prop.cbegin()}, {*ctx, node->prop.cend()}}; + } + + /** + * @brief Lookup utility for meta properties. + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto it = node->prop.find(key); + return it != node->prop.cend() ? meta_prop{*ctx, it->second} : meta_prop{}; + } + + /** + * @brief Returns the next overload of a given function, if any. + * @return The next overload of the given function, if any. + */ + [[nodiscard]] meta_func next() const { + return node->next ? meta_func{*ctx, *node->next} : meta_func{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return (node != nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_func &other) const noexcept { + return (ctx == other.ctx && node == other.node); + } + +private: + const internal::meta_func_node *node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_func &lhs, const meta_func &rhs) noexcept { + return !(lhs == rhs); +} + +/*! @brief Opaque wrapper for types. */ +class meta_type { + template + [[nodiscard]] auto lookup(meta_any *const args, const typename internal::meta_type_node::size_type sz, [[maybe_unused]] bool constness, Func next) const { + decltype(next()) candidate = nullptr; + size_type same{}; + bool ambiguous{}; + + for(auto curr = next(); curr; curr = next()) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(constness && !static_cast(curr->traits & internal::meta_traits::is_const)) { + continue; + } + } + + if(curr->arity == sz) { + size_type match{}; + size_type pos{}; + + for(; pos < sz && args[pos]; ++pos) { + const auto other = curr->arg(*ctx, pos); + const auto type = args[pos].type(); + + if(const auto &info = other.info(); info == type.info()) { + ++match; + } else if(!((type.node.details && (type.node.details->base.contains(info.hash()) || type.node.details->conv.contains(info.hash()))) || (type.node.conversion_helper && other.node.conversion_helper))) { + break; + } + } + + if(pos == sz) { + if(!candidate || match > same) { + candidate = curr; + same = match; + ambiguous = false; + } else if(match == same) { + if constexpr(std::is_same_v, internal::meta_func_node>) { + if(static_cast(curr->traits & internal::meta_traits::is_const) != static_cast(candidate->traits & internal::meta_traits::is_const)) { + candidate = static_cast(candidate->traits & internal::meta_traits::is_const) ? curr : candidate; + ambiguous = false; + continue; + } + } + + ambiguous = true; + } + } + } + } + + return ambiguous ? nullptr : candidate; + } + +public: + /*! @brief Unsigned integer type. */ + using size_type = typename internal::meta_type_node::size_type; + + /*! @brief Default constructor. */ + meta_type() noexcept + : node{}, + ctx{} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_type_node &curr) noexcept + : node{curr}, + ctx{&area} {} + + /** + * @brief Context aware constructor for meta objects. + * @param area The context from which to search for meta types. + * @param curr The underlying node with which to construct the instance. + */ + meta_type(const meta_ctx &area, const internal::meta_base_node &curr) noexcept + : meta_type{area, curr.type(internal::meta_context::from(area))} {} + + /** + * @brief Returns the type info object of the underlying type. + * @return The type info object of the underlying type. + */ + [[nodiscard]] const type_info &info() const noexcept { + return *node.info; + } + + /** + * @brief Returns the identifier assigned to a type. + * @return The identifier assigned to the type. + */ + [[nodiscard]] id_type id() const noexcept { + return node.id; + } + + /** + * @brief Returns the size of the underlying type if known. + * @return The size of the underlying type if known, 0 otherwise. + */ + [[nodiscard]] size_type size_of() const noexcept { + return node.size_of; + } + + /** + * @brief Checks whether a type refers to an arithmetic type or not. + * @return True if the underlying type is an arithmetic type, false + * otherwise. + */ + [[nodiscard]] bool is_arithmetic() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_arithmetic); + } + + /** + * @brief Checks whether a type refers to an integral type or not. + * @return True if the underlying type is an integral type, false otherwise. + */ + [[nodiscard]] bool is_integral() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_integral); + } + + /** + * @brief Checks whether a type refers to a signed type or not. + * @return True if the underlying type is a signed type, false otherwise. + */ + [[nodiscard]] bool is_signed() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_signed); + } + + /** + * @brief Checks whether a type refers to an array type or not. + * @return True if the underlying type is an array type, false otherwise. + */ + [[nodiscard]] bool is_array() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_array); + } + + /** + * @brief Checks whether a type refers to an enum or not. + * @return True if the underlying type is an enum, false otherwise. + */ + [[nodiscard]] bool is_enum() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_enum); + } + + /** + * @brief Checks whether a type refers to a class or not. + * @return True if the underlying type is a class, false otherwise. + */ + [[nodiscard]] bool is_class() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_class); + } + + /** + * @brief Checks whether a type refers to a pointer or not. + * @return True if the underlying type is a pointer, false otherwise. + */ + [[nodiscard]] bool is_pointer() const noexcept { + return node.info && (node.info->hash() != remove_pointer().info().hash()); + } + + /** + * @brief Provides the type for which the pointer is defined. + * @return The type for which the pointer is defined or this type if it + * doesn't refer to a pointer type. + */ + [[nodiscard]] meta_type remove_pointer() const noexcept { + return {*ctx, node.remove_pointer(internal::meta_context::from(*ctx))}; // NOLINT + } + + /** + * @brief Checks whether a type is a pointer-like type or not. + * @return True if the underlying type is pointer-like, false otherwise. + */ + [[nodiscard]] bool is_pointer_like() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_pointer_like); + } + + /** + * @brief Checks whether a type refers to a sequence container or not. + * @return True if the type is a sequence container, false otherwise. + */ + [[nodiscard]] bool is_sequence_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_sequence_container); + } + + /** + * @brief Checks whether a type refers to an associative container or not. + * @return True if the type is an associative container, false otherwise. + */ + [[nodiscard]] bool is_associative_container() const noexcept { + return static_cast(node.traits & internal::meta_traits::is_meta_associative_container); + } + + /** + * @brief Checks whether a type refers to a recognized class template + * specialization or not. + * @return True if the type is a recognized class template specialization, + * false otherwise. + */ + [[nodiscard]] bool is_template_specialization() const noexcept { + return (node.templ.arity != 0u); + } + + /** + * @brief Returns the number of template arguments. + * @return The number of template arguments. + */ + [[nodiscard]] size_type template_arity() const noexcept { + return node.templ.arity; + } + + /** + * @brief Returns a tag for the class template of the underlying type. + * @return The tag for the class template of the underlying type. + */ + [[nodiscard]] inline meta_type template_type() const noexcept { + return node.templ.type ? meta_type{*ctx, node.templ.type(internal::meta_context::from(*ctx))} : meta_type{}; + } + + /** + * @brief Returns the type of the i-th template argument of a type. + * @param index Index of the template argument of which to return the type. + * @return The type of the i-th template argument of a type. + */ + [[nodiscard]] inline meta_type template_arg(const size_type index) const noexcept { + return index < template_arity() ? meta_type{*ctx, node.templ.arg(internal::meta_context::from(*ctx), index)} : meta_type{}; + } + + /** + * @brief Checks if a type supports direct casting to another type. + * @param other The meta type to test for. + * @return True if direct casting is allowed, false otherwise. + */ + [[nodiscard]] bool can_cast(const meta_type &other) const noexcept { + // casting this is UB in all cases but we aren't going to use the resulting pointer, so... + return (internal::try_cast(internal::meta_context::from(*ctx), node, other.node, this) != nullptr); + } + + /** + * @brief Checks if a type supports conversion it to another type. + * @param other The meta type to test for. + * @return True if the conversion is allowed, false otherwise. + */ + [[nodiscard]] bool can_convert(const meta_type &other) const noexcept { + return (internal::try_convert(internal::meta_context::from(*ctx), node, other.info(), other.is_arithmetic() || other.is_enum(), nullptr, [](const void *, auto &&...args) { return ((static_cast(args), 1) + ... + 0u); }) != 0u); + } + + /** + * @brief Returns a range to visit registered top-level base meta types. + * @return An iterable range to visit registered top-level base meta types. + */ + [[nodiscard]] meta_range base() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->base.cbegin()}, {*ctx, node.details->base.cend()}} : range_type{}; + } + + /** + * @brief Returns a range to visit registered top-level meta data. + * @return An iterable range to visit registered top-level meta data. + */ + [[nodiscard]] meta_range data() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->data.cbegin()}, {*ctx, node.details->data.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta data (bases are also visited). + * @param id Unique identifier. + * @return The registered meta data for the given identifier, if any. + */ + [[nodiscard]] meta_data data(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::data>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_data{*ctx, *elem} : meta_data{}; + } + + /** + * @brief Returns a range to visit registered top-level functions. + * @return An iterable range to visit registered top-level functions. + */ + [[nodiscard]] meta_range func() const noexcept { + using return_type = meta_range; + return node.details ? return_type{{*ctx, node.details->func.cbegin()}, {*ctx, node.details->func.cend()}} : return_type{}; + } + + /** + * @brief Lookup utility for meta functions (bases are also visited). + * + * In case of overloaded functions, a random one is returned. + * + * @param id Unique identifier. + * @return The registered meta function for the given identifier, if any. + */ + [[nodiscard]] meta_func func(const id_type id) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::func>(internal::meta_context::from(*ctx), node, id); + return elem ? meta_func{*ctx, *elem} : meta_func{}; + } + + /** + * @brief Creates an instance of the underlying type, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param args Parameters to use to construct the instance. + * @param sz Number of parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + [[nodiscard]] meta_any construct(meta_any *const args, const size_type sz) const { + if(node.details) { + if(const auto *candidate = lookup(args, sz, false, [first = node.details->ctor.cbegin(), last = node.details->ctor.cend()]() mutable { return first == last ? nullptr : &(first++)->second; }); candidate) { + return candidate->invoke(*ctx, args); + } + } + + if(sz == 0u && node.default_constructor) { + return node.default_constructor(*ctx); + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief construct + * @tparam Args Types of arguments to use to construct the instance. + * @param args Parameters to use to construct the instance. + * @return A wrapper containing the new instance, if any. + */ + template + [[nodiscard]] meta_any construct(Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return construct(arguments, sizeof...(Args)); + } + + /** + * @brief Wraps an opaque element of the underlying type. + * @param element A valid pointer to an element of the underlying type. + * @return A wrapper that references the given instance. + */ + [[nodiscard]] meta_any from_void(void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, element, nullptr) : meta_any{meta_ctx_arg, *ctx}; + } + + /*! @copydoc from_void */ + [[nodiscard]] meta_any from_void(const void *element) const { + return (element && node.from_void) ? node.from_void(*ctx, nullptr, element) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Invokes a function given an identifier, if possible. + * + * @warning + * The context of the arguments is **never** changed. + * + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @param sz Number of parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + meta_any invoke(const id_type id, meta_handle instance, meta_any *const args, const size_type sz) const { + if(node.details) { + if(auto it = node.details->func.find(id); it != node.details->func.cend()) { + if(const auto *candidate = lookup(args, sz, instance && (instance->data() == nullptr), [curr = &it->second]() mutable { return curr ? std::exchange(curr, curr->next.get()) : nullptr; }); candidate) { + return candidate->invoke(*ctx, meta_handle{*ctx, std::move(instance)}, args); + } + } + } + + for(auto &&curr: base()) { + if(auto elem = curr.second.invoke(id, *instance.operator->(), args, sz); elem) { + return elem; + } + } + + return meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @copybrief invoke + * @param id Unique identifier. + * @tparam Args Types of arguments to use to invoke the function. + * @param instance An opaque instance of the underlying type. + * @param args Parameters to use to invoke the function. + * @return A wrapper containing the returned value, if any. + */ + template + meta_any invoke(const id_type id, meta_handle instance, Args &&...args) const { + meta_any arguments[sizeof...(Args) + !sizeof...(Args)]{{*ctx, std::forward(args)}...}; + return invoke(id, std::move(instance), arguments, sizeof...(Args)); + } + + /** + * @brief Sets the value of a given variable. + * @tparam Type Type of value to assign. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @param value Parameter to use to set the underlying variable. + * @return True in case of success, false otherwise. + */ + template + bool set(const id_type id, meta_handle instance, Type &&value) const { + const auto candidate = data(id); + return candidate && candidate.set(std::move(instance), std::forward(value)); + } + + /** + * @brief Gets the value of a given variable. + * @param id Unique identifier. + * @param instance An opaque instance of the underlying type. + * @return A wrapper containing the value of the underlying variable. + */ + [[nodiscard]] meta_any get(const id_type id, meta_handle instance) const { + const auto candidate = data(id); + return candidate ? candidate.get(std::move(instance)) : meta_any{meta_ctx_arg, *ctx}; + } + + /** + * @brief Returns a range to visit registered top-level meta properties. + * @return An iterable range to visit registered top-level meta properties. + */ + [[nodiscard]] meta_range prop() const noexcept { + using range_type = meta_range; + return node.details ? range_type{{*ctx, node.details->prop.cbegin()}, {*ctx, node.details->prop.cend()}} : range_type{}; + } + + /** + * @brief Lookup utility for meta properties (bases are also visited). + * @param key The key to use to search for a property. + * @return The registered meta property for the given key, if any. + */ + [[nodiscard]] meta_prop prop(const id_type key) const { + const auto *elem = internal::look_for<&internal::meta_type_descriptor::prop>(internal::meta_context::from(*ctx), node, key); + return elem ? meta_prop{*ctx, *elem} : meta_prop{}; + } + + /** + * @brief Returns true if an object is valid, false otherwise. + * @return True if the object is valid, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return !(ctx == nullptr); + } + + /*! @copydoc meta_prop::operator== */ + [[nodiscard]] bool operator==(const meta_type &other) const noexcept { + return (ctx == other.ctx) && ((!node.info && !other.node.info) || (node.info && other.node.info && *node.info == *other.node.info)); + } + +private: + internal::meta_type_node node; + const meta_ctx *ctx; +}; + +/** + * @brief Checks if two objects refer to the same type. + * @param lhs An object, either valid or not. + * @param rhs An object, either valid or not. + * @return False if the objects refer to the same node, true otherwise. + */ +[[nodiscard]] inline bool operator!=(const meta_type &lhs, const meta_type &rhs) noexcept { + return !(lhs == rhs); +} + +[[nodiscard]] inline meta_type meta_any::type() const noexcept { + return node.info ? meta_type{*ctx, node} : meta_type{}; +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) const { + return type().invoke(id, *this, std::forward(args)...); +} + +template +meta_any meta_any::invoke(const id_type id, Args &&...args) { + return type().invoke(id, *this, std::forward(args)...); +} + +template +bool meta_any::set(const id_type id, Type &&value) { + return type().set(id, *this, std::forward(value)); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) const { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::get(const id_type id) { + return type().get(id, *this); +} + +[[nodiscard]] inline meta_any meta_any::allow_cast(const meta_type &type) const { + return internal::try_convert(internal::meta_context::from(*ctx), node, type.info(), type.is_arithmetic() || type.is_enum(), data(), [this, &type]([[maybe_unused]] const void *instance, auto &&...args) { + if constexpr((std::is_same_v>, internal::meta_type_node> || ...)) { + return (args.from_void(*ctx, nullptr, instance), ...); + } else if constexpr((std::is_same_v>, internal::meta_conv_node> || ...)) { + return (args.conv(*ctx, instance), ...); + } else if constexpr((std::is_same_v>, decltype(internal::meta_type_node::conversion_helper)> || ...)) { + // exploits the fact that arithmetic types and enums are also default constructible + auto other = type.construct(); + const auto value = (args(nullptr, instance), ...); + other.node.conversion_helper(other.data(), &value); + return other; + } else { + // forwards to force a compile-time error in case of available arguments + return meta_any{meta_ctx_arg, *ctx, std::forward(args)...}; + } + }); +} + +inline bool meta_any::assign(const meta_any &other) { + auto value = other.allow_cast({*ctx, node}); + return value && storage.assign(value.storage); +} + +inline bool meta_any::assign(meta_any &&other) { + if(*node.info == *other.node.info) { + return storage.assign(std::move(other.storage)); + } + + return assign(std::as_const(other)); +} + +[[nodiscard]] inline meta_type meta_data::type() const noexcept { + return meta_type{*ctx, node->type(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_data::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +[[nodiscard]] inline meta_type meta_func::ret() const noexcept { + return meta_type{*ctx, node->ret(internal::meta_context::from(*ctx))}; +} + +[[nodiscard]] inline meta_type meta_func::arg(const size_type index) const noexcept { + return index < arity() ? node->arg(*ctx, index) : meta_type{}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +class meta_sequence_container::meta_iterator final { + using vtable_type = void(const void *, const std::ptrdiff_t, meta_any *); + + template + static void basic_vtable(const void *value, const std::ptrdiff_t offset, meta_any *other) { + const auto &it = *static_cast(value); + other ? other->emplace(*it) : std::advance(const_cast(it), offset); + } + +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; + using iterator_concept = std::bidirectional_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), 1, nullptr); + return *this; + } + + meta_iterator operator++(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), ++value, nullptr); + return orig; + } + + meta_iterator &operator--() noexcept { + vtable(handle.data(), -1, nullptr); + return *this; + } + + meta_iterator operator--(int value) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), --value, nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{meta_ctx_arg, *ctx}; + vtable(handle.data(), 0, &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + + [[nodiscard]] const any &base() const noexcept { + return handle; + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; + +class meta_associative_container::meta_iterator final { + using vtable_type = void(const void *, std::pair *); + + template + static void basic_vtable(const void *value, std::pair *other) { + if(const auto &it = *static_cast(value); other) { + if constexpr(KeyOnly) { + other->first.emplace(*it); + } else { + other->first.emplacefirst))>(it->first); + other->second.emplacesecond))>(it->second); + } + } else { + ++const_cast(it); + } + } + +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; + using iterator_concept = std::forward_iterator_tag; + + meta_iterator() noexcept + : meta_iterator{locator::value_or()} {} + + meta_iterator(const meta_ctx &area) noexcept + : ctx{&area} {} + + template + meta_iterator(const meta_ctx &area, std::bool_constant, It iter) noexcept + : ctx{&area}, + vtable{&basic_vtable}, + handle{iter} {} + + meta_iterator &operator++() noexcept { + vtable(handle.data(), nullptr); + return *this; + } + + meta_iterator operator++(int) noexcept { + meta_iterator orig = *this; + vtable(handle.data(), nullptr); + return orig; + } + + [[nodiscard]] reference operator*() const { + reference other{{meta_ctx_arg, *ctx}, {meta_ctx_arg, *ctx}}; + vtable(handle.data(), &other); + return other; + } + + [[nodiscard]] pointer operator->() const { + return operator*(); + } + + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(handle); + } + + [[nodiscard]] bool operator==(const meta_iterator &other) const noexcept { + return handle == other.handle; + } + + [[nodiscard]] bool operator!=(const meta_iterator &other) const noexcept { + return !(*this == other); + } + +private: + const meta_ctx *ctx{}; + vtable_type *vtable{}; + any handle{}; +}; +/*! @endcond */ + +/** + * @brief Returns the meta value type of a container. + * @return The meta value type of the container. + */ +[[nodiscard]] inline meta_type meta_sequence_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the size of a container. + * @return The size of the container. + */ +[[nodiscard]] inline meta_sequence_container::size_type meta_sequence_container::size() const noexcept { + return size_fn(data); +} + +/** + * @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 !const_only && resize_fn(const_cast(data), sz); +} + +/** + * @brief Clears the content of a container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/** + * @brief Reserves storage for at least the given number of elements. + * @param sz The new capacity of the container. + * @return True in case of success, false otherwise. + */ +inline bool meta_sequence_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/** + * @brief Returns an iterator to the first element of a container. + * @return An iterator to the first element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Returns an iterator that is past the last element of a container. + * @return An iterator that is past the last element of the container. + */ +[[nodiscard]] inline meta_sequence_container::iterator meta_sequence_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @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) { + // this abomination is necessary because only on macos value_type and const_reference are different types for std::vector + if(const auto vtype = value_type_node(internal::meta_context::from(*ctx)); !const_only && (value.allow_cast({*ctx, vtype}) || value.allow_cast({*ctx, const_reference_node(internal::meta_context::from(*ctx))}))) { + const bool is_value_type = (value.type().info() == *vtype.info); + return insert_fn(*ctx, const_cast(data), is_value_type ? std::as_const(value).data() : nullptr, is_value_type ? nullptr : std::as_const(value).data(), it); + } + + return iterator{*ctx}; +} + +/** + * @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 const_only ? iterator{*ctx} : erase_fn(*ctx, const_cast(data), it); +} + +/** + * @brief Returns a reference to the element at a given location of a container + * (no bounds checking is performed). + * @param pos The position of the element to return. + * @return A reference to the requested element properly wrapped. + */ +[[nodiscard]] inline meta_any meta_sequence_container::operator[](const size_type pos) { + auto it = begin(); + it.operator++(static_cast(pos) - 1); + return *it; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_sequence_container::operator bool() const noexcept { + return (data != nullptr); +} + +/** + * @brief Returns true if a container is also key-only, false otherwise. + * @return True if the associative container is also key-only, false otherwise. + */ +[[deprecated("use mapped_type() instead")]] [[nodiscard]] inline bool meta_associative_container::key_only() const noexcept { + return (mapped_type_node == nullptr); +} + +/** + * @brief Returns the meta key type of a container. + * @return The meta key type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::key_type() const noexcept { + return key_type_node ? meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/** + * @brief Returns the meta mapped type of a container. + * @return The meta mapped type of the a container. + */ +[[nodiscard]] inline meta_type meta_associative_container::mapped_type() const noexcept { + return mapped_type_node ? meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::value_type */ +[[nodiscard]] inline meta_type meta_associative_container::value_type() const noexcept { + return value_type_node ? meta_type{*ctx, value_type_node(internal::meta_context::from(*ctx))} : meta_type{}; +} + +/*! @copydoc meta_sequence_container::size */ +[[nodiscard]] inline meta_associative_container::size_type meta_associative_container::size() const noexcept { + return size_fn(data); +} + +/*! @copydoc meta_sequence_container::clear */ +inline bool meta_associative_container::clear() { + return !const_only && clear_fn(const_cast(data)); +} + +/*! @copydoc meta_sequence_container::reserve */ +inline bool meta_associative_container::reserve(const size_type sz) { + return !const_only && reserve_fn(const_cast(data), sz); +} + +/*! @copydoc meta_sequence_container::begin */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::begin() { + return begin_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/*! @copydoc meta_sequence_container::end */ +[[nodiscard]] inline meta_associative_container::iterator meta_associative_container::end() { + return end_fn(*ctx, const_only ? nullptr : const_cast(data), data); +} + +/** + * @brief Inserts a key-only or key/value element into a container. + * @param key The key of the element to insert. + * @param value The value of the element to insert, if needed. + * @return A bool denoting whether the insertion took place. + */ +inline bool meta_associative_container::insert(meta_any key, meta_any value = {}) { + return !const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) + && (!mapped_type_node || value.allow_cast(meta_type{*ctx, mapped_type_node(internal::meta_context::from(*ctx))})) + && insert_fn(const_cast(data), std::as_const(key).data(), std::as_const(value).data()); +} + +/** + * @brief Removes the specified element from a container. + * @param key The key of the element to remove. + * @return A bool denoting whether the removal took place. + */ +inline meta_associative_container::size_type meta_associative_container::erase(meta_any key) { + return (!const_only && key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))})) ? erase_fn(const_cast(data), std::as_const(key).data()) : 0u; +} + +/** + * @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 key.allow_cast(meta_type{*ctx, key_type_node(internal::meta_context::from(*ctx))}) ? find_fn(*ctx, const_only ? nullptr : const_cast(data), data, std::as_const(key).data()) : iterator{*ctx}; +} + +/** + * @brief Returns false if a proxy is invalid, true otherwise. + * @return False if the proxy is invalid, true otherwise. + */ +[[nodiscard]] inline meta_associative_container::operator bool() const noexcept { + return (data != nullptr); +} + +} // namespace entt + +#endif + +// #include "meta/node.hpp" +#ifndef ENTT_META_NODE_HPP +#define ENTT_META_NODE_HPP + +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../container/dense_map.hpp" + +// #include "../core/attribute.h" + +// #include "../core/enum.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/type_traits.hpp" + +// #include "../core/utility.hpp" + +// #include "context.hpp" + +// #include "type_traits.hpp" + + +namespace entt { + +class meta_any; +class meta_type; +struct meta_handle; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class meta_traits : std::uint32_t { + is_none = 0x0000, + is_const = 0x0001, + is_static = 0x0002, + is_arithmetic = 0x0004, + is_integral = 0x0008, + is_signed = 0x0010, + is_array = 0x0020, + is_enum = 0x0040, + is_class = 0x0080, + is_meta_pointer_like = 0x0100, + is_meta_sequence_container = 0x0200, + is_meta_associative_container = 0x0400, + _entt_enum_as_bitmask +}; + +struct meta_type_node; + +struct meta_prop_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + std::shared_ptr value{}; +}; + +struct meta_base_node { + meta_type_node (*type)(const meta_context &) noexcept {}; + const void *(*cast)(const void *) noexcept {}; +}; + +struct meta_conv_node { + meta_any (*conv)(const meta_ctx &, const void *){}; +}; + +struct meta_ctor_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_any *const){}; +}; + +struct meta_dtor_node { + void (*dtor)(void *){}; +}; + +struct meta_data_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + bool (*set)(meta_handle, meta_any){}; + meta_any (*get)(const meta_ctx &, meta_handle){}; + dense_map prop{}; +}; + +struct meta_func_node { + using size_type = std::size_t; + + meta_traits traits{meta_traits::is_none}; + size_type arity{0u}; + meta_type_node (*ret)(const meta_context &) noexcept {}; + meta_type (*arg)(const meta_ctx &, const size_type) noexcept {}; + meta_any (*invoke)(const meta_ctx &, meta_handle, meta_any *const){}; + std::shared_ptr next{}; + dense_map prop{}; +}; + +struct meta_template_node { + using size_type = std::size_t; + + size_type arity{0u}; + meta_type_node (*type)(const meta_context &) noexcept {}; + meta_type_node (*arg)(const meta_context &, const size_type) noexcept {}; +}; + +struct meta_type_descriptor { + dense_map ctor{}; + dense_map base{}; + dense_map conv{}; + dense_map data{}; + dense_map func{}; + dense_map prop{}; +}; + +struct meta_type_node { + using size_type = std::size_t; + + const type_info *info{}; + id_type id{}; + meta_traits traits{meta_traits::is_none}; + size_type size_of{0u}; + meta_type_node (*resolve)(const meta_context &) noexcept {}; + meta_type_node (*remove_pointer)(const meta_context &) noexcept {}; + meta_any (*default_constructor)(const meta_ctx &){}; + double (*conversion_helper)(void *, const void *){}; + meta_any (*from_void)(const meta_ctx &, void *, const void *){}; + meta_template_node templ{}; + meta_dtor_node dtor{}; + std::shared_ptr details{}; +}; + +template +auto *look_for(const meta_context &context, const meta_type_node &node, const id_type id) { + if(node.details) { + if(const auto it = (node.details.get()->*Member).find(id); it != (node.details.get()->*Member).cend()) { + return &it->second; + } + + for(auto &&curr: node.details->base) { + if(auto *elem = look_for(context, curr.second.type(context), id); elem) { + return elem; + } + } + } + + return static_cast*Member)>::mapped_type *>(nullptr); +} + +template +meta_type_node resolve(const meta_context &) noexcept; + +template +[[nodiscard]] auto meta_arg_node(const meta_context &context, type_list, [[maybe_unused]] const std::size_t index) noexcept { + [[maybe_unused]] std::size_t pos{}; + meta_type_node (*value)(const meta_context &) noexcept = nullptr; + ((value = (pos++ == index ? &resolve>> : value)), ...); + ENTT_ASSERT(value != nullptr, "Out of bounds"); + return value(context); +} + +[[nodiscard]] inline const void *try_cast(const meta_context &context, const meta_type_node &from, const meta_type_node &to, const void *instance) noexcept { + if(from.info && to.info && *from.info == *to.info) { + return instance; + } + + if(from.details) { + for(auto &&curr: from.details->base) { + if(const void *elem = try_cast(context, curr.second.type(context), to, curr.second.cast(instance)); elem) { + return elem; + } + } + } + + return nullptr; +} + +template +[[nodiscard]] inline auto try_convert(const meta_context &context, const meta_type_node &from, const type_info &to, const bool arithmetic_or_enum, const void *instance, Func func) { + if(from.info && *from.info == to) { + return func(instance, from); + } + + if(from.details) { + if(auto it = from.details->conv.find(to.hash()); it != from.details->conv.cend()) { + return func(instance, it->second); + } + + for(auto &&curr: from.details->base) { + if(auto other = try_convert(context, curr.second.type(context), to, arithmetic_or_enum, curr.second.cast(instance), func); other) { + return other; + } + } + } + + if(from.conversion_helper && arithmetic_or_enum) { + return func(instance, from.conversion_helper); + } + + return func(instance); +} + +[[nodiscard]] inline const meta_type_node *try_resolve(const meta_context &context, const type_info &info) noexcept { + const auto it = context.value.find(info.hash()); + return it != context.value.end() ? &it->second : nullptr; +} + +template +[[nodiscard]] meta_type_node resolve(const meta_context &context) noexcept { + static_assert(std::is_same_v>>, "Invalid type"); + + if(auto *elem = try_resolve(context, type_id()); elem) { + return *elem; + } + + meta_type_node node{ + &type_id(), + type_id().hash(), + (std::is_arithmetic_v ? meta_traits::is_arithmetic : meta_traits::is_none) + | (std::is_integral_v ? meta_traits::is_integral : meta_traits::is_none) + | (std::is_signed_v ? meta_traits::is_signed : meta_traits::is_none) + | (std::is_array_v ? meta_traits::is_array : meta_traits::is_none) + | (std::is_enum_v ? meta_traits::is_enum : meta_traits::is_none) + | (std::is_class_v ? meta_traits::is_class : meta_traits::is_none) + | (is_meta_pointer_like_v ? meta_traits::is_meta_pointer_like : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_sequence_container : meta_traits::is_none) + | (is_complete_v> ? meta_traits::is_meta_associative_container : meta_traits::is_none), + size_of_v, + &resolve, + &resolve>>}; + + if constexpr(std::is_default_constructible_v) { + node.default_constructor = +[](const meta_ctx &ctx) { + return meta_any{ctx, std::in_place_type}; + }; + } + + if constexpr(std::is_arithmetic_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(*static_cast(value))) : static_cast(*static_cast(value)); + }; + } else if constexpr(std::is_enum_v) { + node.conversion_helper = +[](void *bin, const void *value) { + return bin ? static_cast(*static_cast(bin) = static_cast(static_cast>(*static_cast(value)))) : static_cast(*static_cast(value)); + }; + } + + if constexpr(!std::is_void_v && !std::is_function_v) { + node.from_void = +[](const meta_ctx &ctx, void *element, const void *as_const) { + if(element) { + return meta_any{ctx, std::in_place_type &>, *static_cast *>(element)}; + } + + return meta_any{ctx, std::in_place_type &>, *static_cast *>(as_const)}; + }; + } + + if constexpr(is_complete_v>) { + node.templ = meta_template_node{ + meta_template_traits::args_type::size, + &resolve::class_type>, + +[](const meta_context &area, const std::size_t index) noexcept { return meta_arg_node(area, typename meta_template_traits::args_type{}, index); }}; + } + + return node; +} + +} // namespace internal +/*! @endcond */ + +} // namespace entt + +#endif + +// #include "meta/pointer.hpp" +#ifndef ENTT_META_POINTER_HPP +#define ENTT_META_POINTER_HPP + +#include +#include +// #include "type_traits.hpp" + + +namespace entt { + +/** + * @brief Makes plain pointers pointer-like types for the meta system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like + : std::true_type {}; + +/** + * @brief Partial specialization used to reject pointers to arrays. + * @tparam Type Type of elements of the array. + * @tparam N Number of elements of the array. + */ +template +struct is_meta_pointer_like + : std::false_type {}; + +/** + * @brief Makes `std::shared_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +/** + * @brief Makes `std::unique_ptr`s of any type pointer-like types for the meta + * system. + * @tparam Type Element type. + * @tparam Args Other arguments. + */ +template +struct is_meta_pointer_like> + : std::true_type {}; + +} // namespace entt + +#endif + +// #include "meta/policy.hpp" +#ifndef ENTT_META_POLICY_HPP +#define ENTT_META_POLICY_HPP + +#include + +namespace entt { + +/*! @brief Empty class type used to request the _as ref_ policy. */ +struct as_ref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v && !std::is_const_v>; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as cref_ policy. */ +struct as_cref_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = std::is_reference_v; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as-is_ policy. */ +struct as_is_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/*! @brief Empty class type used to request the _as void_ policy. */ +struct as_void_t final { + /*! @cond TURN_OFF_DOXYGEN */ + template + static constexpr bool value = true; + /*! @endcond */ +}; + +/** + * @brief Provides the member constant `value` to true if a type also is a meta + * policy, false otherwise. + * @tparam Type Type to check. + */ +template +struct is_meta_policy + : std::bool_constant || std::is_same_v || std::is_same_v || std::is_same_v> {}; + +/** + * @brief Helper variable template. + * @tparam Type Type to check. + */ +template +inline constexpr bool is_meta_policy_v = is_meta_policy::value; + +} // namespace entt + +#endif + +// #include "meta/range.hpp" +#ifndef ENTT_META_RANGE_HPP +#define ENTT_META_RANGE_HPP + +#include +#include +#include +// #include "../core/fwd.hpp" + +// #include "../core/iterator.hpp" + +// #include "context.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct meta_range_iterator final { + using difference_type = std::ptrdiff_t; + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::random_access_iterator_tag; + + constexpr meta_range_iterator() noexcept + : it{}, + ctx{} {} + + constexpr meta_range_iterator(const meta_ctx &area, const It iter) noexcept + : it{iter}, + ctx{&area} {} + + constexpr meta_range_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr meta_range_iterator operator++(int) noexcept { + meta_range_iterator orig = *this; + return ++(*this), orig; + } + + constexpr meta_range_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr meta_range_iterator operator--(int) noexcept { + meta_range_iterator orig = *this; + return operator--(), orig; + } + + constexpr meta_range_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr meta_range_iterator operator+(const difference_type value) const noexcept { + meta_range_iterator copy = *this; + return (copy += value); + } + + constexpr meta_range_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr meta_range_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, Type{*ctx, it[value].second}}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->first, Type{*ctx, it->second}}; + } + + template + friend constexpr std::ptrdiff_t operator-(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator==(const meta_range_iterator &, const meta_range_iterator &) noexcept; + + template + friend constexpr bool operator<(const meta_range_iterator &, const meta_range_iterator &) noexcept; + +private: + It it; + const meta_ctx *ctx; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const meta_range_iterator &lhs, const meta_range_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Iterable range to use to iterate all types of meta objects. + * @tparam Type Type of meta objects returned. + * @tparam It Type of forward iterator. + */ +template +using meta_range = iterable_adaptor>; + +} // namespace entt + +#endif + +// #include "meta/resolve.hpp" +#ifndef ENTT_META_RESOLVE_HPP +#define ENTT_META_RESOLVE_HPP + +#include +// #include "../core/type_info.hpp" + +// #include "../locator/locator.hpp" + +// #include "context.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "range.hpp" + + +namespace entt { + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @param ctx The context from which to search for meta types. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::resolve>>(context)}; +} + +/** + * @brief Returns the meta type associated with a given type. + * @tparam Type Type to use to search for a meta type. + * @return The meta type associated with the given type, if any. + */ +template +[[nodiscard]] meta_type resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns a range to use to visit all meta types. + * @param ctx The context from which to search for meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve(const meta_ctx &ctx) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {{ctx, context.value.cbegin()}, {ctx, context.value.cend()}}; +} + +/** + * @brief Returns a range to use to visit all meta types. + * @return An iterable range to use to visit all meta types. + */ +[[nodiscard]] inline meta_range resolve() noexcept { + return resolve(locator::value_or()); +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param ctx The context from which to search for meta types. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const id_type id) noexcept { + for(auto &&curr: resolve(ctx)) { + if(curr.second.id() == id) { + return curr.second; + } + } + + return meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given identifier, if any. + * @param id Unique identifier. + * @return The meta type associated with the given identifier, if any. + */ +[[nodiscard]] inline meta_type resolve(const id_type id) noexcept { + return resolve(locator::value_or(), id); +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param ctx The context from which to search for meta types. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const meta_ctx &ctx, const type_info &info) noexcept { + auto &&context = internal::meta_context::from(ctx); + const auto *elem = internal::try_resolve(context, info); + return elem ? meta_type{ctx, *elem} : meta_type{}; +} + +/** + * @brief Returns the meta type associated with a given type info object. + * @param info The type info object of the requested type. + * @return The meta type associated with the given type info object, if any. + */ +[[nodiscard]] inline meta_type resolve(const type_info &info) noexcept { + return resolve(locator::value_or(), info); +} + +} // namespace entt + +#endif + +// #include "meta/template.hpp" +#ifndef ENTT_META_TEMPLATE_HPP +#define ENTT_META_TEMPLATE_HPP + +// #include "../core/type_traits.hpp" + + +namespace entt { + +/*! @brief Utility class to disambiguate class templates. */ +template class> +struct meta_class_template_tag {}; + +/** + * @brief General purpose traits class for generating meta template information. + * @tparam Clazz Type of class template. + * @tparam Args Types of template arguments. + */ +template class Clazz, typename... Args> +struct meta_template_traits> { + /*! @brief Wrapped class template. */ + using class_type = meta_class_template_tag; + /*! @brief List of template arguments. */ + using args_type = type_list; +}; + +} // namespace entt + +#endif + +// #include "meta/type_traits.hpp" +#ifndef ENTT_META_TYPE_TRAITS_HPP +#define ENTT_META_TYPE_TRAITS_HPP + +#include +#include + +namespace entt { + +/** + * @brief Traits class template to be specialized to enable support for meta + * template information. + */ +template +struct meta_template_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * sequence containers. + */ +template +struct meta_sequence_container_traits; + +/** + * @brief Traits class template to be specialized to enable support for meta + * associative containers. + */ +template +struct meta_associative_container_traits; + +/** + * @brief Provides the member constant `value` to true if a given type is a + * pointer-like type from the point of view of the meta system, false otherwise. + */ +template +struct is_meta_pointer_like: std::false_type {}; + +/** + * @brief Partial specialization to ensure that const pointer-like types are + * also accepted. + * @tparam Type Potentially pointer-like type. + */ +template +struct is_meta_pointer_like: is_meta_pointer_like {}; + +/** + * @brief Helper variable template. + * @tparam Type Potentially pointer-like type. + */ +template +inline constexpr auto is_meta_pointer_like_v = is_meta_pointer_like::value; + +} // namespace entt + +#endif + +// #include "meta/utility.hpp" +#ifndef ENTT_META_UTILITY_HPP +#define ENTT_META_UTILITY_HPP + +#include +#include +#include +#include +// #include "../core/type_traits.hpp" + +// #include "../locator/locator.hpp" + +// #include "meta.hpp" + +// #include "node.hpp" + +// #include "policy.hpp" + + +namespace entt { + +/** + * @brief Meta function descriptor traits. + * @tparam Ret Function return type. + * @tparam Args Function arguments. + * @tparam Static Function staticness. + * @tparam Const Function constness. + */ +template +struct meta_function_descriptor_traits { + /*! @brief Meta function return type. */ + using return_type = Ret; + /*! @brief Meta function arguments. */ + using args_type = Args; + + /*! @brief True if the meta function is static, false otherwise. */ + static constexpr bool is_static = Static; + /*! @brief True if the meta function is const, false otherwise. */ + static constexpr bool is_const = Const; +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct meta_function_descriptor; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + true> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam Class Actual owner of the member function. + * @tparam Args Function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t, type_list, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta data is associated. + * @tparam Class Actual owner of the data member. + * @tparam Ret Data member type. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret &, + std::conditional_t, type_list<>, type_list>, + !std::is_base_of_v, + false> {}; + +/** + * @brief Meta function descriptor. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Ret Function return type. + * @tparam MaybeType First function argument. + * @tparam Args Other function arguments. + */ +template +struct meta_function_descriptor + : meta_function_descriptor_traits< + Ret, + std::conditional_t< + std::is_same_v>, Type> || std::is_base_of_v>, Type>, + type_list, + type_list>, + !(std::is_same_v>, Type> || std::is_base_of_v>, Type>), + std::is_const_v> && (std::is_same_v>, Type> || 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 + : meta_function_descriptor_traits< + Ret, + type_list<>, + true, + false> {}; + +/** + * @brief Meta function helper. + * + * Converts a function type to be associated with a reflected type into its meta + * function descriptor. + * + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +class meta_function_helper { + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...) const); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (Class::*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret Class::*); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Ret (*)(Args...)); + + template + static constexpr meta_function_descriptor get_rid_of_noexcept(Class); + +public: + /*! @brief The meta function descriptor of the given function. */ + using type = decltype(get_rid_of_noexcept(std::declval())); +}; + +/** + * @brief Helper type. + * @tparam Type Reflected type to which the meta function is associated. + * @tparam Candidate The actual function to associate with the reflected type. + */ +template +using meta_function_helper_t = typename meta_function_helper::type; + +/** + * @brief Wraps a value depending on the given policy. + * + * This function always returns a wrapped value in the requested context.
+ * Therefore, if the passed value is itself a wrapped object with a different + * context, it undergoes a rebinding to the requested context. + * + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param ctx The context from which to search for meta types. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(const meta_ctx &ctx, [[maybe_unused]] Type &&value) { + if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type}; + } else if constexpr(std::is_same_v) { + return meta_any{ctx, std::in_place_type, value}; + } else if constexpr(std::is_same_v) { + static_assert(std::is_lvalue_reference_v, "Invalid type"); + return meta_any{ctx, std::in_place_type &>, std::as_const(value)}; + } else { + return meta_any{ctx, std::forward(value)}; + } +} + +/** + * @brief Wraps a value depending on the given policy. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Type Type of value to wrap. + * @param value Value to wrap. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_dispatch(Type &&value) { + return meta_dispatch(locator::value_or(), std::forward(value)); +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param ctx The context from which to search for meta types. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const meta_ctx &ctx, const std::size_t index) noexcept { + auto &&context = internal::meta_context::from(ctx); + return {ctx, internal::meta_arg_node(context, Type{}, index)}; +} + +/** + * @brief Returns the meta type of the i-th element of a list of arguments. + * @tparam Type Type list of the actual types of arguments. + * @param index The index of the element for which to return the meta type. + * @return The meta type of the i-th element of the list of arguments. + */ +template +[[nodiscard]] static meta_type meta_arg(const std::size_t index) noexcept { + return meta_arg(locator::value_or(), index); +} + +/** + * @brief Sets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to set. + * @param instance An opaque instance of the underlying type, if required. + * @param value Parameter to use to set the variable. + * @return True in case of success, false otherwise. + */ +template +[[nodiscard]] bool meta_setter([[maybe_unused]] meta_handle instance, [[maybe_unused]] meta_any value) { + if constexpr(!std::is_same_v && !std::is_same_v) { + if constexpr(std::is_member_function_pointer_v || std::is_function_v>>) { + using descriptor = meta_function_helper_t; + using data_type = type_list_element_t; + + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz, value.cast()); + return true; + } + } else if constexpr(std::is_member_object_pointer_v) { + using data_type = std::remove_reference_t::return_type>; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(auto *const clazz = instance->try_cast(); clazz && value.allow_cast()) { + std::invoke(Data, *clazz) = value.cast(); + return true; + } + } + } else { + using data_type = std::remove_reference_t; + + if constexpr(!std::is_array_v && !std::is_const_v) { + if(value.allow_cast()) { + *Data = value.cast(); + return true; + } + } + } + } + + return false; +} + +/** + * @brief Gets the value of a given variable. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance) { + if constexpr(std::is_member_pointer_v || std::is_function_v>>) { + if constexpr(!std::is_array_v>>>) { + if constexpr(std::is_invocable_v) { + if(auto *clazz = instance->try_cast(); clazz) { + return meta_dispatch(ctx, std::invoke(Data, *clazz)); + } + } + + if constexpr(std::is_invocable_v) { + if(auto *fallback = instance->try_cast(); fallback) { + return meta_dispatch(ctx, std::invoke(Data, *fallback)); + } + } + } + + return meta_any{meta_ctx_arg, ctx}; + } else if constexpr(std::is_pointer_v) { + if constexpr(std::is_array_v>) { + return meta_any{meta_ctx_arg, ctx}; + } else { + return meta_dispatch(ctx, *Data); + } + } else { + return meta_dispatch(ctx, Data); + } +} + +/** + * @brief Gets the value of a given variable. + * @tparam Type Reflected type to which the variable is associated. + * @tparam Data The actual variable to get. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @return A meta any containing the value of the underlying variable. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_getter(meta_handle instance) { + return meta_getter(locator::value_or(), std::move(instance)); +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +[[nodiscard]] meta_any meta_invoke_with_args(const meta_ctx &ctx, Candidate &&candidate, Args &&...args) { + if constexpr(std::is_void_v(candidate), args...))>) { + std::invoke(std::forward(candidate), args...); + return meta_any{ctx, std::in_place_type}; + } else { + return meta_dispatch(ctx, std::invoke(std::forward(candidate), args...)); + } +} + +template +[[nodiscard]] meta_any meta_invoke(const meta_ctx &ctx, [[maybe_unused]] meta_handle instance, Candidate &&candidate, [[maybe_unused]] meta_any *args, std::index_sequence) { + using descriptor = meta_function_helper_t>; + + if constexpr(std::is_invocable_v, const Type &, type_list_element_t...>) { + if(const auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else if constexpr(std::is_invocable_v, Type &, type_list_element_t...>) { + if(auto *const clazz = instance->try_cast(); clazz && ((args + Index)->allow_cast>() && ...)) { // NOLINT + return meta_invoke_with_args(ctx, std::forward(candidate), *clazz, (args + Index)->cast>()...); + } + } else { + if(((args + Index)->allow_cast>() && ...)) { + return meta_invoke_with_args(ctx, std::forward(candidate), (args + Index)->cast>()...); + } + } + + return meta_any{meta_ctx_arg, ctx}; +} + +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args, std::index_sequence) { + if(((args + Index)->allow_cast() && ...)) { + return meta_any{ctx, std::in_place_type, (args + Index)->cast()...}; + } + + return meta_any{meta_ctx_arg, ctx}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, Candidate &&candidate, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to _invoke_ an object given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param instance An opaque instance of the underlying type, if required. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, Candidate &&candidate, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), std::forward(candidate), args); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(const meta_ctx &ctx, meta_handle instance, meta_any *const args) { + return internal::meta_invoke(ctx, std::move(instance), Candidate, args, std::make_index_sequence>::args_type::size>{}); +} + +/** + * @brief Tries to invoke a function given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param instance An opaque instance of the underlying type, if required. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_invoke(meta_handle instance, meta_any *const args) { + return meta_invoke(locator::value_or(), std::move(instance), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, meta_any *const args) { + return internal::meta_construct(ctx, args, std::index_sequence_for{}); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Actual type of the instance to construct. + * @tparam Args Types of arguments expected. + * @param args Parameters to use to construct the instance. + * @return A meta any containing the new instance, if any. + */ +template +[[nodiscard]] meta_any meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param ctx The context from which to search for meta types. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] meta_any meta_construct(const meta_ctx &ctx, Candidate &&candidate, meta_any *const args) { + if constexpr(meta_function_helper_t::is_static || std::is_class_v>>) { + return internal::meta_invoke(ctx, {}, std::forward(candidate), args, std::make_index_sequence>::args_type::size>{}); + } else { + return internal::meta_invoke(ctx, *args, std::forward(candidate), args + 1u, std::make_index_sequence>::args_type::size>{}); + } +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the object to _invoke_ is associated. + * @tparam Policy Optional policy (no policy set by default). + * @tparam Candidate The type of the actual object to _invoke_. + * @param candidate The actual object to _invoke_. + * @param args Parameters to use to _invoke_ the object. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(Candidate &&candidate, meta_any *const args) { + return meta_construct(locator::value_or(), std::forward(candidate), args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * + * @warning + * The context provided is used only for the return type.
+ * It's up to the caller to bind the arguments to the right context(s). + * + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param ctx The context from which to search for meta types. + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(const meta_ctx &ctx, meta_any *const args) { + return meta_construct(ctx, Candidate, args); +} + +/** + * @brief Tries to construct an instance given a list of erased parameters. + * @tparam Type Reflected type to which the function is associated. + * @tparam Candidate The actual function to invoke. + * @tparam Policy Optional policy (no policy set by default). + * @param args Parameters to use to invoke the function. + * @return A meta any containing the returned value, if any. + */ +template +[[nodiscard]] std::enable_if_t, meta_any> meta_construct(meta_any *const args) { + return meta_construct(locator::value_or(), args); +} + +} // namespace entt + +#endif + +// #include "platform/android-ndk-r17.hpp" +#ifndef ENTT_PLATFORM_ANDROID_NDK_R17_HPP +#define ENTT_PLATFORM_ANDROID_NDK_R17_HPP + +/*! @cond TURN_OFF_DOXYGEN */ +#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 +/*! @endcond */ + +#endif + +// #include "poly/poly.hpp" +#ifndef ENTT_POLY_POLY_HPP +#define ENTT_POLY_POLY_HPP + +#include +#include +#include +#include +#include +// #include "../core/any.hpp" +#ifndef ENTT_CORE_ANY_HPP +#define ENTT_CORE_ANY_HPP + +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +enum class any_operation : std::uint8_t { + copy, + move, + transfer, + assign, + destroy, + compare, + get +}; + +} // namespace internal +/*! @endcond */ + +/*! @brief Possible modes of an any object. */ +enum class any_policy : std::uint8_t { + /*! @brief Default mode, the object owns the contained element. */ + owner, + /*! @brief Aliasing mode, the object _points_ to a non-const element. */ + ref, + /*! @brief Const aliasing mode, the object _points_ to a const element. */ + cref +}; + +/** + * @brief A SBO friendly, type-safe container for single values of any type. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_any { + using operation = internal::any_operation; + using vtable_type = const void *(const operation, const basic_any &, const void *); + + struct storage_type { + alignas(Align) std::byte data[Len + !Len]; + }; + + template + static constexpr bool in_situ = Len && alignof(Type) <= Align && sizeof(Type) <= Len && std::is_nothrow_move_constructible_v; + + template + static const void *basic_vtable(const operation op, const basic_any &value, const void *other) { + static_assert(!std::is_void_v && std::is_same_v>, Type>, "Invalid type"); + const Type *element = nullptr; + + if constexpr(in_situ) { + element = (value.mode == any_policy::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.mode == any_policy::owner) { + return new(&static_cast(const_cast(other))->storage) Type{std::move(*const_cast(element))}; + } + } + + return (static_cast(const_cast(other))->instance = std::exchange(const_cast(value).instance, nullptr)); + case operation::transfer: + if constexpr(std::is_move_assignable_v) { + *const_cast(element) = std::move(*static_cast(const_cast(other))); + return other; + } + [[fallthrough]]; + case operation::assign: + if constexpr(std::is_copy_assignable_v) { + *const_cast(element) = *static_cast(other); + return other; + } + break; + case operation::destroy: + if constexpr(in_situ) { + element->~Type(); + } else if constexpr(std::is_array_v) { + delete[] element; + } else { + delete element; + } + break; + case operation::compare: + if constexpr(!std::is_function_v && !std::is_array_v && is_equality_comparable_v) { + return *element == *static_cast(other) ? other : nullptr; + } else { + return (element == other) ? other : nullptr; + } + case operation::get: + return element; + } + + return nullptr; + } + + template + void initialize([[maybe_unused]] Args &&...args) { + info = &type_id>>(); + + if constexpr(!std::is_void_v) { + vtable = basic_vtable>>; + + if constexpr(std::is_lvalue_reference_v) { + static_assert((std::is_lvalue_reference_v && ...) && (sizeof...(Args) == 1u), "Invalid arguments"); + mode = std::is_const_v> ? any_policy::cref : any_policy::ref; + instance = (std::addressof(args), ...); + } else if constexpr(in_situ>>) { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + new(&storage) std::remove_cv_t>{std::forward(args)...}; + } else { + new(&storage) std::remove_cv_t>(std::forward(args)...); + } + } else { + if constexpr(std::is_aggregate_v>> && (sizeof...(Args) != 0u || !std::is_default_constructible_v>>)) { + instance = new std::remove_cv_t>{std::forward(args)...}; + } else { + instance = new std::remove_cv_t>(std::forward(args)...); + } + } + } + } + + basic_any(const basic_any &other, const any_policy pol) noexcept + : instance{other.data()}, + info{other.info}, + vtable{other.vtable}, + mode{pol} {} + +public: + /*! @brief Size of the internal storage. */ + static constexpr auto length = Len; + /*! @brief Alignment requirement. */ + static constexpr auto alignment = Align; + + /*! @brief Default constructor. */ + constexpr basic_any() noexcept + : basic_any{std::in_place_type} {} + + /** + * @brief Constructs a wrapper by directly initializing the new object. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_any(std::in_place_type_t, Args &&...args) + : instance{}, + info{}, + vtable{}, + mode{any_policy::owner} { + initialize(std::forward(args)...); + } + + /** + * @brief Constructs a wrapper from a given value. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + */ + template, basic_any>>> + basic_any(Type &&value) + : basic_any{std::in_place_type>, std::forward(value)} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + basic_any(const basic_any &other) + : basic_any{} { + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_any(basic_any &&other) noexcept + : instance{}, + info{other.info}, + vtable{other.vtable}, + mode{other.mode} { + if(other.vtable) { + other.vtable(operation::move, other, this); + } + } + + /*! @brief Frees the internal storage, whatever it means. */ + ~basic_any() { + if(vtable && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + } + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This any object. + */ + basic_any &operator=(const basic_any &other) { + reset(); + + if(other.vtable) { + other.vtable(operation::copy, other, this); + } + + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This any object. + */ + basic_any &operator=(basic_any &&other) noexcept { + reset(); + + if(other.vtable) { + other.vtable(operation::move, other, this); + info = other.info; + vtable = other.vtable; + mode = other.mode; + } + + return *this; + } + + /** + * @brief Value assignment operator. + * @tparam Type Type of object to use to initialize the wrapper. + * @param value An instance of an object to use to initialize the wrapper. + * @return This any object. + */ + template + std::enable_if_t, basic_any>, basic_any &> + operator=(Type &&value) { + emplace>(std::forward(value)); + return *this; + } + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return *info; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return vtable ? vtable(operation::get, *this, nullptr) : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data(const type_info &req) const noexcept { + return *info == req ? data() : nullptr; + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data() noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data()); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @param req Expected type. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] void *data(const type_info &req) noexcept { + return mode == any_policy::cref ? nullptr : const_cast(std::as_const(*this).data(req)); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + reset(); + initialize(std::forward(args)...); + } + + /** + * @brief Assigns a value to the contained object without replacing it. + * @param other The value to assign to the contained object. + * @return True in case of success, false otherwise. + */ + bool assign(const basic_any &other) { + if(vtable && mode != any_policy::cref && *info == *other.info) { + return (vtable(operation::assign, *this, other.data()) != nullptr); + } + + return false; + } + + /*! @copydoc assign */ + bool assign(basic_any &&other) { + if(vtable && mode != any_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 && (mode == any_policy::owner)) { + vtable(operation::destroy, *this, nullptr); + } + + // unnecessary but it helps to detect nasty bugs + ENTT_ASSERT((instance = nullptr) == nullptr, ""); + info = &type_id(); + vtable = nullptr; + mode = any_policy::owner; + } + + /** + * @brief Returns false if a wrapper is empty, true otherwise. + * @return False if the wrapper is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return vtable != nullptr; + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return False if the two objects differ in their content, true otherwise. + */ + [[nodiscard]] bool operator==(const basic_any &other) const noexcept { + if(vtable && *info == *other.info) { + return (vtable(operation::compare, *this, other.data()) != nullptr); + } + + return (!vtable && !other.vtable); + } + + /** + * @brief Checks if two wrappers differ in their content. + * @param other Wrapper with which to compare. + * @return True if the two objects differ in their content, false otherwise. + */ + [[nodiscard]] bool operator!=(const basic_any &other) const noexcept { + return !(*this == other); + } + + /** + * @brief Aliasing constructor. + * @return A wrapper that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_any as_ref() noexcept { + return basic_any{*this, (mode == any_policy::cref ? any_policy::cref : any_policy::ref)}; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_any as_ref() const noexcept { + return basic_any{*this, any_policy::cref}; + } + + /** + * @brief Returns true if a wrapper owns its object, false otherwise. + * @return True if the wrapper owns its object, false otherwise. + */ + [[deprecated("use policy() and any_policy instead")]] [[nodiscard]] bool owner() const noexcept { + return (mode == any_policy::owner); + } + + /** + * @brief Returns the current mode of an any object. + * @return The current mode of the any object. + */ + [[nodiscard]] any_policy policy() const noexcept { + return mode; + } + +private: + union { + const void *instance; + storage_type storage; + }; + const type_info *info; + vtable_type *vtable; + any_policy mode; +}; + +/** + * @brief Performs type-safe access to the contained object. + * @tparam Type Type to which conversion is required. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + * @param data Target any object. + * @return The element converted to the requested type. + */ +template +[[nodiscard]] Type any_cast(const basic_any &data) noexcept { + const auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &data) noexcept { + // forces const on non-reference types to make them work also with wrappers for const references + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(*instance); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type any_cast(basic_any &&data) noexcept { + if constexpr(std::is_copy_constructible_v>>) { + if(auto *const instance = any_cast>(&data); instance) { + return static_cast(std::move(*instance)); + } else { + return any_cast(data); + } + } else { + auto *const instance = any_cast>(&data); + ENTT_ASSERT(instance, "Invalid instance"); + return static_cast(std::move(*instance)); + } +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] const Type *any_cast(const basic_any *data) noexcept { + const auto &info = type_id>(); + return static_cast(data->data(info)); +} + +/*! @copydoc any_cast */ +template +[[nodiscard]] Type *any_cast(basic_any *data) noexcept { + if constexpr(std::is_const_v) { + // last attempt to make wrappers for const references return their values + return any_cast(&std::as_const(*data)); + } else { + const auto &info = type_id>(); + return static_cast(data->data(info)); + } +} + +/** + * @brief Constructs a wrapper from a given type, passing it all arguments. + * @tparam Type Type of object to use to initialize the wrapper. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + * @return A properly initialized wrapper for an object of the given type. + */ +template::length, std::size_t Align = basic_any::alignment, typename... Args> +[[nodiscard]] 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> +[[nodiscard]] basic_any forward_as_any(Type &&value) { + return basic_any{std::in_place_type, std::forward(value)}; +} + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_POLY_FWD_HPP +#define ENTT_POLY_FWD_HPP + +#include + +namespace entt { + +template +class basic_poly; + +/** + * @brief Alias declaration for the most common use case. + * @tparam Concept Concept descriptor. + */ +template +using poly = basic_poly; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @brief Inspector class used to infer the type of the virtual table. */ +struct poly_inspector { + /** + * @brief Generic conversion operator (definition only). + * @tparam Type Type to which conversion is requested. + */ + template + operator Type &&() const; + + /** + * @brief Dummy invocation function (definition only). + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param args The arguments to pass to the function. + * @return A poly inspector convertible to any type. + */ + template + poly_inspector invoke(Args &&...args) const; + + /*! @copydoc invoke */ + template + poly_inspector invoke(Args &&...args); +}; + +/** + * @brief Static virtual table factory. + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Alignment requirement. + */ +template +class poly_vtable { + using inspector = typename Concept::template type; + + template + static auto vtable_entry(Ret (*)(inspector &, Args...)) -> Ret (*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret (*)(const inspector &, Args...)) -> Ret (*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret (*)(Args...)) -> Ret (*)(const basic_any &, Args...); + + template + static auto vtable_entry(Ret (inspector::*)(Args...)) -> Ret (*)(basic_any &, Args...); + + template + static auto vtable_entry(Ret (inspector::*)(Args...) const) -> Ret (*)(const basic_any &, Args...); + + template + static auto make_vtable(value_list) noexcept + -> decltype(std::make_tuple(vtable_entry(Candidate)...)); + + template + [[nodiscard]] static constexpr auto make_vtable(type_list) noexcept { + if constexpr(sizeof...(Func) == 0u) { + return decltype(make_vtable(typename Concept::template impl{})){}; + } else if constexpr((std::is_function_v && ...)) { + return decltype(std::make_tuple(vtable_entry(std::declval())...)){}; + } + } + + template + static void fill_vtable_entry(Ret (*&entry)(Any &, Args...)) noexcept { + if constexpr(std::is_invocable_r_v) { + entry = +[](Any &, Args... args) -> Ret { + return std::invoke(Candidate, std::forward(args)...); + }; + } else { + entry = +[](Any &instance, Args... args) -> Ret { + return static_cast(std::invoke(Candidate, any_cast &>(instance), std::forward(args)...)); + }; + } + } + + template + [[nodiscard]] static auto fill_vtable(std::index_sequence) noexcept { + vtable_type impl{}; + (fill_vtable_entry>>(std::get(impl)), ...); + return impl; + } + + using vtable_type = decltype(make_vtable(Concept{})); + static constexpr bool is_mono_v = std::tuple_size_v == 1u; + +public: + /*! @brief Virtual table type. */ + using type = std::conditional_t, const vtable_type *>; + + /** + * @brief Returns a static virtual table for a specific concept and type. + * @tparam Type The type for which to generate the virtual table. + * @return A static virtual table for the given concept and type. + */ + template + [[nodiscard]] static type instance() noexcept { + static_assert(std::is_same_v>, "Type differs from its decayed form"); + static const vtable_type vtable = fill_vtable(std::make_index_sequence::size>{}); + + if constexpr(is_mono_v) { + return std::get<0>(vtable); + } else { + return &vtable; + } + } +}; + +/** + * @brief Poly base class used to inject functionalities into concepts. + * @tparam Poly The outermost poly class. + */ +template +struct poly_base { + /** + * @brief Invokes a function from the static virtual table. + * @tparam Member Index of the function to invoke. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ + template + [[nodiscard]] decltype(auto) invoke(const poly_base &self, Args &&...args) const { + const auto &poly = static_cast(self); + + if constexpr(std::is_function_v>) { + return poly.vtable(poly.storage, std::forward(args)...); + } else { + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + } + + /*! @copydoc invoke */ + template + [[nodiscard]] decltype(auto) invoke(poly_base &self, Args &&...args) { + auto &poly = static_cast(self); + + if constexpr(std::is_function_v>) { + static_assert(Member == 0u, "Unknown member"); + return poly.vtable(poly.storage, std::forward(args)...); + } else { + return std::get(*poly.vtable)(poly.storage, std::forward(args)...); + } + } +}; + +/** + * @brief Shortcut for calling `poly_base::invoke`. + * @tparam Member Index of the function to invoke. + * @tparam Poly A fully defined poly object. + * @tparam Args Types of arguments to pass to the function. + * @param self A reference to the poly object that made the call. + * @param args The arguments to pass to the function. + * @return The return value of the invoked function, if any. + */ +template +decltype(auto) poly_call(Poly &&self, Args &&...args) { + return std::forward(self).template invoke(self, std::forward(args)...); +} + +/** + * @brief Static polymorphism made simple and within everyone's reach. + * + * Static polymorphism is a very powerful tool in C++, albeit sometimes + * cumbersome to obtain.
+ * This class aims to make it simple and easy to use. + * + * @note + * Both deduced and defined static virtual tables are supported.
+ * Moreover, the `poly` class template also works with unmanaged objects. + * + * @tparam Concept Concept descriptor. + * @tparam Len Size of the storage reserved for the small buffer optimization. + * @tparam Align Optional alignment requirement. + */ +template +class basic_poly: private Concept::template type>> { + friend struct poly_base; + +public: + /*! @brief Concept type. */ + using concept_type = typename Concept::template type>; + /*! @brief Virtual table type. */ + using vtable_type = typename poly_vtable::type; + + /*! @brief Default constructor. */ + basic_poly() noexcept + : storage{}, + vtable{} {} + + /** + * @brief Constructs a poly by directly initializing the new object. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + explicit basic_poly(std::in_place_type_t, Args &&...args) + : storage{std::in_place_type, std::forward(args)...}, + vtable{poly_vtable::template instance>>()} {} + + /** + * @brief Constructs a poly from a given value. + * @tparam Type Type of object to use to initialize the poly. + * @param value An instance of an object to use to initialize the poly. + */ + template>, basic_poly>>> + basic_poly(Type &&value) noexcept + : basic_poly{std::in_place_type>>, std::forward(value)} {} + + /** + * @brief Returns the object type if any, `type_id()` otherwise. + * @return The object type if any, `type_id()` otherwise. + */ + [[nodiscard]] const type_info &type() const noexcept { + return storage.type(); + } + + /** + * @brief Returns an opaque pointer to the contained instance. + * @return An opaque pointer the contained instance, if any. + */ + [[nodiscard]] const void *data() const noexcept { + return storage.data(); + } + + /*! @copydoc data */ + [[nodiscard]] void *data() noexcept { + return storage.data(); + } + + /** + * @brief Replaces the contained object by creating a new instance directly. + * @tparam Type Type of object to use to initialize the poly. + * @tparam Args Types of arguments to use to construct the new instance. + * @param args Parameters to use to construct the instance. + */ + template + void emplace(Args &&...args) { + storage.template emplace(std::forward(args)...); + vtable = poly_vtable::template instance>>(); + } + + /*! @brief Destroys contained object */ + void reset() { + storage.reset(); + vtable = {}; + } + + /** + * @brief Returns false if a poly is empty, true otherwise. + * @return False if the poly is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(storage); + } + + /** + * @brief Returns a pointer to the underlying concept. + * @return A pointer to the underlying concept. + */ + [[nodiscard]] concept_type *operator->() noexcept { + return this; + } + + /*! @copydoc operator-> */ + [[nodiscard]] const concept_type *operator->() const noexcept { + return this; + } + + /** + * @brief Aliasing constructor. + * @return A poly that shares a reference to an unmanaged object. + */ + [[nodiscard]] basic_poly as_ref() noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + + /*! @copydoc as_ref */ + [[nodiscard]] basic_poly as_ref() const noexcept { + basic_poly ref{}; + ref.storage = storage.as_ref(); + ref.vtable = vtable; + return ref; + } + +private: + basic_any storage; + vtable_type vtable; +}; + +} // namespace entt + +#endif + +// #include "process/process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +// #include "fwd.hpp" +#ifndef ENTT_PROCESS_FWD_HPP +#define ENTT_PROCESS_FWD_HPP + +#include +#include + +namespace entt { + +template +class process; + +template> +class basic_scheduler; + +/*! @brief Alias declaration for the most common use case. */ +using scheduler = basic_scheduler<>; + +} // namespace entt + +#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 noexcept {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() noexcept { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() noexcept { + if(alive()) { + current = state::failed; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() noexcept { + if(current == state::running) { + current = state::paused; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() noexcept { + if(current == state::paused) { + current = state::running; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() noexcept { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + if(alive()) { + current = state::aborted; + + if(immediate) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const noexcept { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const noexcept { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const noexcept { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const noexcept { + return current == state::rejected; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::uninitialized: + next(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::integral_constant{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + next(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&...args) + : Func{std::forward(args)...} {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()( + delta, + data, + [this]() { this->succeed(); }, + [this]() { this->fail(); }); + } +}; + +} // namespace entt + +#endif + +// #include "process/scheduler.hpp" +#ifndef ENTT_PROCESS_SCHEDULER_HPP +#define ENTT_PROCESS_SCHEDULER_HPP + +#include +#include +#include +#include +#include +// #include "../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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "fwd.hpp" + +// #include "process.hpp" +#ifndef ENTT_PROCESS_PROCESS_HPP +#define ENTT_PROCESS_PROCESS_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Base class for processes. + * + * This class stays true to the CRTP idiom. Derived classes must specify what's + * the intended type for elapsed times.
+ * A process should expose publicly the following member functions whether + * required: + * + * * @code{.cpp} + * void update(Delta, void *); + * @endcode + * + * It's invoked once per tick until a process is explicitly aborted or it + * terminates either with or without errors. Even though it's not mandatory to + * declare this member function, as a rule of thumb each process should at + * least define it to work properly. The `void *` parameter is an opaque + * pointer to user data (if any) forwarded directly to the process during an + * update. + * + * * @code{.cpp} + * void init(); + * @endcode + * + * It's invoked when the process joins the running queue of a scheduler. This + * happens as soon as it's attached to the scheduler if the process is a top + * level one, otherwise when it replaces its parent if the process is a + * continuation. + * + * * @code{.cpp} + * void succeeded(); + * @endcode + * + * It's invoked in case of success, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void failed(); + * @endcode + * + * It's invoked in case of errors, immediately after an update and during the + * same tick. + * + * * @code{.cpp} + * void aborted(); + * @endcode + * + * It's invoked only if a process is explicitly aborted. There is no guarantee + * that it executes in the same tick, this depends solely on whether the + * process is aborted immediately or not. + * + * Derived classes can change the internal state of a process by invoking the + * `succeed` and `fail` protected member functions and even pause or unpause the + * process itself. + * + * @sa scheduler + * + * @tparam Derived Actual type of process that extends the class template. + * @tparam Delta Type to use to provide elapsed time. + */ +template +class process { + enum class state : std::uint8_t { + uninitialized = 0, + running, + paused, + succeeded, + failed, + aborted, + finished, + rejected + }; + + template + auto next(std::integral_constant) + -> decltype(std::declval().init(), void()) { + static_cast(this)->init(); + } + + template + auto next(std::integral_constant, Delta delta, void *data) + -> decltype(std::declval().update(delta, data), void()) { + static_cast(this)->update(delta, data); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().succeeded(), void()) { + static_cast(this)->succeeded(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().failed(), void()) { + static_cast(this)->failed(); + } + + template + auto next(std::integral_constant) + -> decltype(std::declval().aborted(), void()) { + static_cast(this)->aborted(); + } + + void next(...) const noexcept {} + +protected: + /** + * @brief Terminates a process with success if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void succeed() noexcept { + if(alive()) { + current = state::succeeded; + } + } + + /** + * @brief Terminates a process with errors if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + */ + void fail() noexcept { + if(alive()) { + current = state::failed; + } + } + + /** + * @brief Stops a process if it's in a running state. + * + * The function is idempotent and it does nothing if the process isn't + * running. + */ + void pause() noexcept { + if(current == state::running) { + current = state::paused; + } + } + + /** + * @brief Restarts a process if it's paused. + * + * The function is idempotent and it does nothing if the process isn't + * paused. + */ + void unpause() noexcept { + if(current == state::paused) { + current = state::running; + } + } + +public: + /*! @brief Type used to provide elapsed time. */ + using delta_type = Delta; + + /*! @brief Default destructor. */ + virtual ~process() noexcept { + static_assert(std::is_base_of_v, "Incorrect use of the class template"); + } + + /** + * @brief Aborts a process if it's still alive. + * + * The function is idempotent and it does nothing if the process isn't + * alive. + * + * @param immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + if(alive()) { + current = state::aborted; + + if(immediate) { + tick({}); + } + } + } + + /** + * @brief Returns true if a process is either running or paused. + * @return True if the process is still alive, false otherwise. + */ + [[nodiscard]] bool alive() const noexcept { + return current == state::running || current == state::paused; + } + + /** + * @brief Returns true if a process is already terminated. + * @return True if the process is terminated, false otherwise. + */ + [[nodiscard]] bool finished() const noexcept { + return current == state::finished; + } + + /** + * @brief Returns true if a process is currently paused. + * @return True if the process is paused, false otherwise. + */ + [[nodiscard]] bool paused() const noexcept { + return current == state::paused; + } + + /** + * @brief Returns true if a process terminated with errors. + * @return True if the process terminated with errors, false otherwise. + */ + [[nodiscard]] bool rejected() const noexcept { + return current == state::rejected; + } + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void tick(const Delta delta, void *data = nullptr) { + switch(current) { + case state::uninitialized: + next(std::integral_constant{}); + current = state::running; + break; + case state::running: + next(std::integral_constant{}, delta, data); + break; + default: + // suppress warnings + break; + } + + // if it's dead, it must be notified and removed immediately + switch(current) { + case state::succeeded: + next(std::integral_constant{}); + current = state::finished; + break; + case state::failed: + next(std::integral_constant{}); + current = state::rejected; + break; + case state::aborted: + next(std::integral_constant{}); + current = state::rejected; + break; + default: + // suppress warnings + break; + } + } + +private: + state current{state::uninitialized}; +}; + +/** + * @brief Adaptor for lambdas and functors to turn them into processes. + * + * Lambdas and functors can't be used directly with a scheduler for they are not + * properly defined processes with managed life cycles.
+ * This class helps in filling the gap and turning lambdas and functors into + * full featured processes usable by a scheduler. + * + * The signature of the function call operator should be equivalent to the + * following: + * + * @code{.cpp} + * void(Delta delta, void *data, auto succeed, auto fail); + * @endcode + * + * Where: + * + * * `delta` is the elapsed time. + * * `data` is an opaque pointer to user data if any, `nullptr` otherwise. + * * `succeed` is a function to call when a process terminates with success. + * * `fail` is a function to call when a process terminates with errors. + * + * The signature of the function call operator of both `succeed` and `fail` + * is equivalent to the following: + * + * @code{.cpp} + * void(); + * @endcode + * + * Usually users shouldn't worry about creating adaptors. A scheduler will + * create them internally each and avery time a lambda or a functor is used as + * a process. + * + * @sa process + * @sa scheduler + * + * @tparam Func Actual type of process. + * @tparam Delta Type to use to provide elapsed time. + */ +template +struct process_adaptor: process, Delta>, private Func { + /** + * @brief Constructs a process adaptor from a lambda or a functor. + * @tparam Args Types of arguments to use to initialize the actual process. + * @param args Parameters to use to initialize the actual process. + */ + template + process_adaptor(Args &&...args) + : Func{std::forward(args)...} {} + + /** + * @brief Updates a process and its internal state if required. + * @param delta Elapsed time. + * @param data Optional data. + */ + void update(const Delta delta, void *data) { + Func::operator()( + delta, + data, + [this]() { this->succeed(); }, + [this]() { this->fail(); }); + } +}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct basic_process_handler { + virtual ~basic_process_handler() = default; + + virtual bool update(const Delta, void *) = 0; + virtual void abort(const bool) = 0; + + // std::shared_ptr because of its type erased allocator which is useful here + std::shared_ptr next; +}; + +template +struct process_handler final: basic_process_handler { + template + process_handler(Args &&...args) + : process{std::forward(args)...} {} + + bool update(const Delta delta, void *data) override { + if(process.tick(delta, data); process.rejected()) { + this->next.reset(); + } + + return (process.rejected() || process.finished()); + } + + void abort(const bool immediate) override { + process.abort(immediate); + } + + Type process; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @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. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_scheduler { + template + using handler_type = internal::process_handler; + + // std::shared_ptr because of its type erased allocator which is useful here + using process_type = std::shared_ptr>; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc; + using container_type = std::vector; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Unsigned integer type. */ + using delta_type = Delta; + + /*! @brief Default constructor. */ + basic_scheduler() + : basic_scheduler{allocator_type{}} {} + + /** + * @brief Constructs a scheduler with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_scheduler(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_scheduler(basic_scheduler &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_scheduler(basic_scheduler &&other, const allocator_type &allocator) noexcept + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This scheduler. + */ + basic_scheduler &operator=(basic_scheduler &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying a scheduler is not allowed"); + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given scheduler. + * @param other Scheduler to exchange the content with. + */ + void swap(basic_scheduler &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Number of processes currently scheduled. + * @return Number of processes currently scheduled. + */ + [[nodiscard]] size_type size() const noexcept { + return handlers.first().size(); + } + + /** + * @brief Returns true if at least a process is currently scheduled. + * @return True if there are scheduled processes, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + + /** + * @brief Discards all scheduled processes. + * + * Processes aren't aborted. They are discarded along with their children + * and never executed again. + */ + void clear() { + handlers.first().clear(); + } + + /** + * @brief Schedules a process for the next tick. + * + * Returned value can be used to attach a continuation for the last process. + * The continutation is scheduled automatically 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 This process scheduler. + */ + template + basic_scheduler &attach(Args &&...args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + auto &ref = handlers.first().emplace_back(std::allocate_shared>(handlers.second(), std::forward(args)...)); + // forces the process to exit the uninitialized state + ref->update({}, nullptr); + return *this; + } + + /** + * @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 can be used to attach a continuation for the last process. + * The continutation is scheduled automatically 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 This process scheduler. + */ + template + basic_scheduler &attach(Func &&func) { + using Proc = process_adaptor, Delta>; + return attach(std::forward(func)); + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Proc Type of process to use as a continuation. + * @tparam Args Types of arguments to use to initialize the process. + * @param args Parameters to use to initialize the process. + * @return This process scheduler. + */ + template + basic_scheduler &then(Args &&...args) { + static_assert(std::is_base_of_v, Proc>, "Invalid process type"); + ENTT_ASSERT(!handlers.first().empty(), "Process not available"); + auto *curr = handlers.first().back().get(); + for(; curr->next; curr = curr->next.get()) {} + curr->next = std::allocate_shared>(handlers.second(), std::forward(args)...); + return *this; + } + + /** + * @brief Sets a process as a continuation of the last scheduled process. + * @tparam Func Type of process to use as a continuation. + * @param func Either a lambda or a functor to use as a process. + * @return This process scheduler. + */ + template + basic_scheduler &then(Func &&func) { + using Proc = process_adaptor, Delta>; + return then(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_type delta, void *data = nullptr) { + for(auto next = handlers.first().size(); next; --next) { + if(const auto pos = next - 1u; handlers.first()[pos]->update(delta, data)) { + // updating might spawn/reallocate, cannot hold refs until here + if(auto &curr = handlers.first()[pos]; curr->next) { + curr = std::move(curr->next); + // forces the process to exit the uninitialized state + curr->update({}, nullptr); + } else { + curr = std::move(handlers.first().back()); + handlers.first().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 immediate Requests an immediate operation. + */ + void abort(const bool immediate = false) { + for(auto &&curr: handlers.first()) { + curr->abort(immediate); + } + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "resource/cache.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_CACHE_HPP +#define ENTT_RESOURCE_RESOURCE_CACHE_HPP + +#include +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_RESOURCE_FWD_HPP +#define ENTT_RESOURCE_FWD_HPP + +#include + +namespace entt { + +template +struct resource_loader; + +template, typename = std::allocator> +class resource_cache; + +template +class resource; + +} // namespace entt + +#endif + +// #include "loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] const handle_type &handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs 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 resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr resource_cache_iterator() noexcept = default; + + constexpr resource_cache_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr resource_cache_iterator(const resource_cache_iterator, Other> &other) noexcept + : it{other.it} {} + + constexpr resource_cache_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr resource_cache_iterator operator++(int) noexcept { + resource_cache_iterator orig = *this; + return ++(*this), orig; + } + + constexpr resource_cache_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr resource_cache_iterator operator--(int) noexcept { + resource_cache_iterator orig = *this; + return operator--(), orig; + } + + constexpr resource_cache_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr resource_cache_iterator operator+(const difference_type value) const noexcept { + resource_cache_iterator copy = *this; + return (copy += value); + } + + constexpr resource_cache_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr resource_cache_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].first, resource{it[value].second}}; + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return (*this)[0]; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + template + friend constexpr std::ptrdiff_t operator-(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator==(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + + template + friend constexpr bool operator<(const resource_cache_iterator &, const resource_cache_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const resource_cache_iterator &lhs, const resource_cache_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v, "Invalid value type"); + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Resource type. */ + using value_type = Type; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Loader type. */ + using loader_type = Loader; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::resource_cache_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::resource_cache_iterator; + + /*! @brief Default constructor. */ + resource_cache() + : resource_cache{loader_type{}} {} + + /** + * @brief Constructs an empty cache with a given allocator. + * @param allocator The allocator to use. + */ + explicit resource_cache(const allocator_type &allocator) + : resource_cache{loader_type{}, allocator} {} + + /** + * @brief Constructs an empty cache with a given allocator and loader. + * @param callable The loader to use. + * @param allocator The allocator to use. + */ + explicit resource_cache(const loader_type &callable, const allocator_type &allocator = allocator_type{}) + : pool{container_type{allocator}, callable} {} + + /*! @brief Default copy constructor. */ + resource_cache(const resource_cache &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + resource_cache(const resource_cache &other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(other.pool.first(), allocator), std::forward_as_tuple(other.pool.second())} {} + + /*! @brief Default move constructor. */ + resource_cache(resource_cache &&) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + resource_cache(resource_cache &&other, const allocator_type &allocator) + : pool{std::piecewise_construct, std::forward_as_tuple(std::move(other.pool.first()), allocator), std::forward_as_tuple(std::move(other.pool.second()))} {} + + /** + * @brief Default copy assignment operator. + * @return This cache. + */ + resource_cache &operator=(const resource_cache &) = default; + + /** + * @brief Default move assignment operator. + * @return This cache. + */ + resource_cache &operator=(resource_cache &&) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pool.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the cache is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal cache. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return pool.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return pool.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal cache. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return pool.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return pool.first().end(); + } + + /** + * @brief Returns true if a cache contains no resources, false otherwise. + * @return True if the cache contains no resources, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return pool.first().empty(); + } + + /** + * @brief Number of resources managed by a cache. + * @return Number of resources currently stored. + */ + [[nodiscard]] size_type size() const noexcept { + return pool.first().size(); + } + + /*! @brief Clears a cache. */ + void clear() noexcept { + pool.first().clear(); + } + + /** + * @brief Loads a resource, if its identifier does not exist. + * + * Arguments are forwarded directly to the loader and _consumed_ only if the + * resource doesn't already exist. + * + * @warning + * If the resource isn't loaded correctly, the returned handle could be + * invalid and any use of it will result in undefined behavior. + * + * @tparam Args Types of arguments to use to load the resource if required. + * @param id Unique resource identifier. + * @param args Arguments to use to load the resource if required. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair load(const id_type id, Args &&...args) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return {it, false}; + } + + return pool.first().emplace(id, pool.second()(std::forward(args)...)); + } + + /** + * @brief Force loads a resource, if its identifier does not exist. + * @copydetails load + */ + template + std::pair force_load(const id_type id, Args &&...args) { + return {pool.first().insert_or_assign(id, pool.second()(std::forward(args)...)).first, true}; + } + + /** + * @brief Returns a handle for a given resource identifier. + * + * @warning + * There is no guarantee that the returned handle is valid.
+ * If it is not, any use will result in indefinite behavior. + * + * @param id Unique resource identifier. + * @return A handle for the given resource. + */ + [[nodiscard]] resource operator[](const id_type id) const { + if(auto it = pool.first().find(id); it != pool.first().cend()) { + return resource{it->second}; + } + + return {}; + } + + /*! @copydoc operator[] */ + [[nodiscard]] resource operator[](const id_type id) { + if(auto it = pool.first().find(id); it != pool.first().end()) { + return resource{it->second}; + } + + return {}; + } + + /** + * @brief Checks if a cache contains a given identifier. + * @param id Unique resource identifier. + * @return True if the cache contains the resource, false otherwise. + */ + [[nodiscard]] bool contains(const id_type id) const { + return pool.first().contains(id); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (pos - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto it = pool.first().begin(); + return pool.first().erase(it + (first - const_iterator{it}), it + (last - const_iterator{it})); + } + + /** + * @brief Removes the given elements from a cache. + * @param id Unique resource identifier. + * @return Number of resources erased (either 0 or 1). + */ + size_type erase(const id_type id) { + return pool.first().erase(id); + } + + /** + * @brief Returns the loader used to create resources. + * @return The loader used to create resources. + */ + [[nodiscard]] loader_type loader() const { + return pool.second(); + } + +private: + compressed_pair pool; +}; + +} // namespace entt + +#endif + +// #include "resource/loader.hpp" +#ifndef ENTT_RESOURCE_LOADER_HPP +#define ENTT_RESOURCE_LOADER_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Transparent loader for shared resources. + * @tparam Type Type of resources created by the loader. + */ +template +struct resource_loader { + /*! @brief Result type. */ + using result_type = std::shared_ptr; + + /** + * @brief Constructs a shared pointer to a resource from its arguments. + * @tparam Args Types of arguments to use to construct the resource. + * @param args Parameters to use to construct the resource. + * @return A shared pointer to a resource of the given type. + */ + template + result_type operator()(Args &&...args) const { + return std::make_shared(std::forward(args)...); + } +}; + +} // namespace entt + +#endif + +// #include "resource/resource.hpp" +#ifndef ENTT_RESOURCE_RESOURCE_HPP +#define ENTT_RESOURCE_RESOURCE_HPP + +#include +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Basic resource handle. + * + * A handle wraps a resource and extends its lifetime. It also shares the same + * resource with all other handles constructed from the same element.
+ * As a rule of thumb, resources should never be copied nor moved. Handles are + * the way to go to push references around. + * + * @tparam Type Type of resource managed by a handle. + */ +template +class resource { + template + friend class resource; + + template + static constexpr bool is_acceptable_v = !std::is_same_v && std::is_constructible_v; + +public: + /*! @brief Resource type. */ + using element_type = Type; + /*! @brief Handle type. */ + using handle_type = std::shared_ptr; + + /*! @brief Default constructor. */ + resource() noexcept + : value{} {} + + /** + * @brief Creates a handle from a weak pointer, namely a resource. + * @param res A weak pointer to a resource. + */ + explicit resource(handle_type res) noexcept + : value{std::move(res)} {} + + /*! @brief Default copy constructor. */ + resource(const resource &) noexcept = default; + + /*! @brief Default move constructor. */ + resource(resource &&) noexcept = default; + + /** + * @brief Aliasing constructor. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle with which to share ownership information. + * @param res Unrelated and unmanaged resources. + */ + template + resource(const resource &other, element_type &res) noexcept + : value{other.value, std::addressof(res)} {} + + /** + * @brief Copy constructs a handle which shares ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + */ + template>> + resource(const resource &other) noexcept + : value{other.value} {} + + /** + * @brief Move constructs a handle which takes ownership of the resource. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + */ + template>> + resource(resource &&other) noexcept + : value{std::move(other.value)} {} + + /** + * @brief Default copy assignment operator. + * @return This resource handle. + */ + resource &operator=(const resource &) noexcept = default; + + /** + * @brief Default move assignment operator. + * @return This resource handle. + */ + resource &operator=(resource &&) noexcept = default; + + /** + * @brief Copy assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to copy from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(const resource &other) noexcept { + value = other.value; + return *this; + } + + /** + * @brief Move assignment operator from foreign handle. + * @tparam Other Type of resource managed by the received handle. + * @param other The handle to move from. + * @return This resource handle. + */ + template + std::enable_if_t, resource &> + operator=(resource &&other) noexcept { + value = std::move(other.value); + return *this; + } + + /** + * @brief Returns a reference to the managed resource. + * + * @warning + * The behavior is undefined if the handle doesn't contain a resource. + * + * @return A reference to the managed resource. + */ + [[nodiscard]] element_type &operator*() const noexcept { + return *value; + } + + /*! @copydoc operator* */ + [[nodiscard]] operator element_type &() const noexcept { + return *value; + } + + /** + * @brief Returns a pointer to the managed resource. + * @return A pointer to the managed resource. + */ + [[nodiscard]] element_type *operator->() const noexcept { + return value.get(); + } + + /** + * @brief Returns true if a handle contains a resource, false otherwise. + * @return True if the handle contains a resource, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(value); + } + + /** + * @brief Returns the underlying resource handle. + * @return The underlying resource handle. + */ + [[nodiscard]] const handle_type &handle() const noexcept { + return value; + } + +private: + handle_type value; +}; + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if both handles refer to the same resource, false otherwise. + */ +template +[[nodiscard]] bool operator==(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) == std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs 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 resource, true otherwise. + */ +template +[[nodiscard]] bool operator!=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than the second, false otherwise. + */ +template +[[nodiscard]] bool operator<(const resource &lhs, const resource &rhs) noexcept { + return (std::addressof(*lhs) < std::addressof(*rhs)); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than the second, false otherwise. + */ +template +[[nodiscard]] bool operator>(const resource &lhs, const resource &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] bool operator<=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs > rhs); +} + +/** + * @brief Compares two handles. + * @tparam Lhs Type of resource managed by the first handle. + * @tparam Rhs Type of resource managed by the second handle. + * @param lhs A valid handle. + * @param rhs A valid handle. + * @return True if the first handle is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] bool operator>=(const resource &lhs, const resource &rhs) noexcept { + return !(lhs < rhs); +} + +} // namespace entt + +#endif + +// #include "signal/delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_SIGNAL_FWD_HPP +#define ENTT_SIGNAL_FWD_HPP + +#include + +namespace entt { + +template +class delegate; + +template> +class basic_dispatcher; + +template> +class emitter; + +class connection; + +struct scoped_connection; + +template +class sink; + +template> +class sigh; + +/*! @brief Alias declaration for the most common use case. */ +using dispatcher = basic_dispatcher<>; + +/*! @brief Disambiguation tag for constructors and the like. */ +template +struct connect_arg_t { + /*! @brief Default constructor. */ + explicit connect_arg_t() = default; +}; + +/** + * @brief Constant of type connect_arg_t used to disambiguate calls. + * @tparam Candidate Element to connect (likely a free or member function). + */ +template +inline constexpr connect_arg_t connect_arg{}; + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "signal/dispatcher.hpp" +#ifndef ENTT_SIGNAL_DISPATCHER_HPP +#define ENTT_SIGNAL_DISPATCHER_HPP + +#include +#include +#include +#include +#include +#include +// #include "../container/dense_map.hpp" +#ifndef ENTT_CONTAINER_DENSE_MAP_HPP +#define ENTT_CONTAINER_DENSE_MAP_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" +#ifndef ENTT_CONFIG_CONFIG_H +#define ENTT_CONFIG_CONFIG_H + +// #include "version.h" +#ifndef ENTT_CONFIG_VERSION_H +#define ENTT_CONFIG_VERSION_H + +// #include "macro.h" +#ifndef ENTT_CONFIG_MACRO_H +#define ENTT_CONFIG_MACRO_H + +#define ENTT_STR(arg) #arg +#define ENTT_XSTR(arg) ENTT_STR(arg) + +#endif + + +#define ENTT_VERSION_MAJOR 3 +#define ENTT_VERSION_MINOR 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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 13 +#define ENTT_VERSION_PATCH 1 + +#define ENTT_VERSION \ + ENTT_XSTR(ENTT_VERSION_MAJOR) \ + "." ENTT_XSTR(ENTT_VERSION_MINOR) "." ENTT_XSTR(ENTT_VERSION_PATCH) + +#endif + + +#if defined(__cpp_exceptions) && !defined(ENTT_NOEXCEPTION) +# define ENTT_CONSTEXPR +# define ENTT_THROW throw +# define ENTT_TRY try +# define ENTT_CATCH catch(...) +#else +# define ENTT_CONSTEXPR constexpr // use only with throwing functions (waiting for C++20) +# define ENTT_THROW +# define ENTT_TRY if(true) +# define ENTT_CATCH if(false) +#endif + +#ifdef ENTT_USE_ATOMIC +# include +# define ENTT_MAYBE_ATOMIC(Type) std::atomic +#else +# define ENTT_MAYBE_ATOMIC(Type) Type +#endif + +#ifndef ENTT_ID_TYPE +# include +# define ENTT_ID_TYPE std::uint32_t +#else +# include // provides coverage for types in the std namespace +#endif + +#ifndef ENTT_SPARSE_PAGE +# define ENTT_SPARSE_PAGE 4096 +#endif + +#ifndef ENTT_PACKED_PAGE +# define ENTT_PACKED_PAGE 1024 +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT +# define ENTT_ASSERT(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT +# include +# define ENTT_ASSERT(condition, msg) assert(condition) +#endif + +#ifdef ENTT_DISABLE_ASSERT +# undef ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) (void(0)) +#elif !defined ENTT_ASSERT_CONSTEXPR +# define ENTT_ASSERT_CONSTEXPR(condition, msg) ENTT_ASSERT(condition, msg) +#endif + +#define ENTT_FAIL(msg) ENTT_ASSERT(false, msg); + +#ifdef ENTT_NO_ETO +# define ENTT_ETO_TYPE(Type) void +#else +# define ENTT_ETO_TYPE(Type) Type +#endif + +#ifdef ENTT_STANDARD_CPP +# define ENTT_NONSTD false +#else +# define ENTT_NONSTD true +# if defined __clang__ || defined __GNUC__ +# define ENTT_PRETTY_FUNCTION __PRETTY_FUNCTION__ +# define ENTT_PRETTY_FUNCTION_PREFIX '=' +# define ENTT_PRETTY_FUNCTION_SUFFIX ']' +# elif defined _MSC_VER +# define ENTT_PRETTY_FUNCTION __FUNCSIG__ +# define ENTT_PRETTY_FUNCTION_PREFIX '<' +# define ENTT_PRETTY_FUNCTION_SUFFIX '>' +# endif +#endif + +#if defined _MSC_VER +# pragma detect_mismatch("entt.version", ENTT_VERSION) +# pragma detect_mismatch("entt.noexcept", ENTT_XSTR(ENTT_TRY)) +# pragma detect_mismatch("entt.id", ENTT_XSTR(ENTT_ID_TYPE)) +# pragma detect_mismatch("entt.nonstd", ENTT_XSTR(ENTT_NONSTD)) +#endif + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + + +namespace entt { + +/** + * @brief Utility class to disambiguate overloaded functions. + * @tparam N Number of choices available. + */ +template +struct choice_t + // unfortunately, doxygen cannot parse such a construct + : /*! @cond TURN_OFF_DOXYGEN */ choice_t /*! @endcond */ +{}; + +/*! @copybrief choice_t */ +template<> +struct choice_t<0> {}; + +/** + * @brief Variable template for the choice trick. + * @tparam N Number of choices available. + */ +template +inline constexpr choice_t choice{}; + +/** + * @brief Identity type trait. + * + * Useful to establish non-deduced contexts in template argument deduction + * (waiting for C++20) or to provide types through function arguments. + * + * @tparam Type A type. + */ +template +struct type_identity { + /*! @brief Identity type. */ + using type = Type; +}; + +/** + * @brief Helper type. + * @tparam Type A type. + */ +template +using type_identity_t = typename type_identity::type; + +/** + * @brief A type-only `sizeof` wrapper that returns 0 where `sizeof` complains. + * @tparam Type The type of which to return the size. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/iterator.hpp" +#ifndef ENTT_CORE_ITERATOR_HPP +#define ENTT_CORE_ITERATOR_HPP + +#include +#include +#include +#include + +namespace entt { + +/** + * @brief Helper type to use as pointer with input iterators. + * @tparam Type of wrapped value. + */ +template +struct input_iterator_pointer final { + /*! @brief Value type. */ + using value_type = Type; + /*! @brief Pointer type. */ + using pointer = Type *; + /*! @brief Reference type. */ + using reference = Type &; + + /** + * @brief Constructs a proxy object by move. + * @param val Value to use to initialize the proxy object. + */ + constexpr input_iterator_pointer(value_type &&val) noexcept(std::is_nothrow_move_constructible_v) + : value{std::move(val)} {} + + /** + * @brief Access operator for accessing wrapped values. + * @return A pointer to the wrapped value. + */ + [[nodiscard]] constexpr pointer operator->() noexcept { + return std::addressof(value); + } + + /** + * @brief Dereference operator for accessing wrapped values. + * @return A reference to the wrapped value. + */ + [[nodiscard]] constexpr reference operator*() noexcept { + return value; + } + +private: + Type value; +}; + +/** + * @brief Plain iota iterator (waiting for C++20). + * @tparam Type Value type. + */ +template +class iota_iterator final { + static_assert(std::is_integral_v, "Not an integral type"); + +public: + /*! @brief Value type, likely an integral one. */ + using value_type = Type; + /*! @brief Invalid pointer type. */ + using pointer = void; + /*! @brief Non-reference type, same as value type. */ + using reference = value_type; + /*! @brief Difference type. */ + using difference_type = std::ptrdiff_t; + /*! @brief Iterator category. */ + using iterator_category = std::input_iterator_tag; + + /*! @brief Default constructor. */ + constexpr iota_iterator() noexcept + : current{} {} + + /** + * @brief Constructs an iota iterator from a given value. + * @param init The initial value assigned to the iota iterator. + */ + constexpr iota_iterator(const value_type init) noexcept + : current{init} {} + + /** + * @brief Pre-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator &operator++() noexcept { + return ++current, *this; + } + + /** + * @brief Post-increment operator. + * @return This iota iterator. + */ + constexpr iota_iterator operator++(int) noexcept { + iota_iterator orig = *this; + return ++(*this), orig; + } + + /** + * @brief Dereference operator. + * @return The underlying value. + */ + [[nodiscard]] constexpr reference operator*() const noexcept { + return current; + } + +private: + value_type current; +}; + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return *lhs == *rhs; +} + +/** + * @brief Comparison operator. + * @tparam Type Value type of the iota iterator. + * @param lhs A properly initialized iota iterator. + * @param rhs A properly initialized iota iterator. + * @return True if the two iterators differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const iota_iterator &lhs, const iota_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Utility class to create an iterable object from a pair of iterators. + * @tparam It Type of iterator. + * @tparam Sentinel Type of sentinel. + */ +template +struct iterable_adaptor final { + /*! @brief Value type. */ + using value_type = typename std::iterator_traits::value_type; + /*! @brief Iterator type. */ + using iterator = It; + /*! @brief Sentinel type. */ + using sentinel = Sentinel; + + /*! @brief Default constructor. */ + constexpr iterable_adaptor() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first{}, + last{} {} + + /** + * @brief Creates an iterable object from a pair of iterators. + * @param from Begin iterator. + * @param to End iterator. + */ + constexpr iterable_adaptor(iterator from, sentinel to) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) + : first{std::move(from)}, + last{std::move(to)} {} + + /** + * @brief Returns an iterator to the beginning. + * @return An iterator to the first element of the range. + */ + [[nodiscard]] constexpr iterator begin() const noexcept { + return first; + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last element of the + * range. + */ + [[nodiscard]] constexpr sentinel end() const noexcept { + return last; + } + + /*! @copydoc begin */ + [[nodiscard]] constexpr iterator cbegin() const noexcept { + return begin(); + } + + /*! @copydoc end */ + [[nodiscard]] constexpr sentinel cend() const noexcept { + return end(); + } + +private: + It first; + Sentinel last; +}; + +} // namespace entt + +#endif + +// #include "../core/memory.hpp" +#ifndef ENTT_CORE_MEMORY_HPP +#define ENTT_CORE_MEMORY_HPP + +#include +#include +#include +#include +#include +#include +// #include "../config/config.h" + + +namespace entt { + +/** + * @brief Checks whether a value is a power of two or not (waiting for C++20 and + * `std::has_single_bit`). + * @param value A value that may or may not be a power of two. + * @return True if the value is a power of two, false otherwise. + */ +[[nodiscard]] inline constexpr bool is_power_of_two(const std::size_t value) noexcept { + return value && ((value & (value - 1)) == 0); +} + +/** + * @brief Computes the smallest power of two greater than or equal to a value + * (waiting for C++20 and `std::bit_ceil`). + * @param value The value to use. + * @return The smallest power of two greater than or equal to the given value. + */ +[[nodiscard]] inline constexpr std::size_t next_power_of_two(const std::size_t value) noexcept { + ENTT_ASSERT_CONSTEXPR(value < (std::size_t{1u} << (std::numeric_limits::digits - 1)), "Numeric limits exceeded"); + std::size_t curr = value - (value != 0u); + + for(int next = 1; next < std::numeric_limits::digits; next = next * 2) { + curr |= curr >> next; + } + + return ++curr; +} + +/** + * @brief Fast module utility function (powers of two only). + * @param value A value for which to calculate the modulus. + * @param mod _Modulus_, it must be a power of two. + * @return The common remainder. + */ +[[nodiscard]] inline constexpr std::size_t fast_mod(const std::size_t value, const std::size_t mod) noexcept { + ENTT_ASSERT_CONSTEXPR(is_power_of_two(mod), "Value must be a power of two"); + return value & (mod - 1u); +} + +/** + * @brief Unwraps fancy pointers, does nothing otherwise (waiting for C++20). + * @tparam Type Pointer type. + * @param ptr Fancy or raw pointer. + * @return A raw pointer that represents the address of the original pointer. + */ +template +[[nodiscard]] constexpr auto to_address(Type &&ptr) noexcept { + if constexpr(std::is_pointer_v>) { + return ptr; + } else { + return to_address(std::forward(ptr).operator->()); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_copy_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_copy_assignment::value) { + lhs = rhs; + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_move_assignment([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_move_assignment::value) { + lhs = std::move(rhs); + } +} + +/** + * @brief Utility function to design allocation-aware containers. + * @tparam Allocator Type of allocator. + * @param lhs A valid allocator. + * @param rhs Another valid allocator. + */ +template +constexpr void propagate_on_container_swap([[maybe_unused]] Allocator &lhs, [[maybe_unused]] Allocator &rhs) noexcept { + if constexpr(std::allocator_traits::propagate_on_container_swap::value) { + using std::swap; + swap(lhs, rhs); + } else { + ENTT_ASSERT_CONSTEXPR(lhs == rhs, "Cannot swap the containers"); + } +} + +/** + * @brief Deleter for allocator-aware unique pointers (waiting for C++20). + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +struct allocation_deleter: private Allocator { + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Pointer type. */ + using pointer = typename std::allocator_traits::pointer; + + /** + * @brief Inherited constructors. + * @param alloc The allocator to use. + */ + constexpr allocation_deleter(const allocator_type &alloc) noexcept(std::is_nothrow_copy_constructible_v) + : Allocator{alloc} {} + + /** + * @brief Destroys the pointed object and deallocates its memory. + * @param ptr A valid pointer to an object of the given type. + */ + constexpr void operator()(pointer ptr) noexcept(std::is_nothrow_destructible_v) { + using alloc_traits = std::allocator_traits; + alloc_traits::destroy(*this, to_address(ptr)); + alloc_traits::deallocate(*this, ptr, 1u); + } +}; + +/** + * @brief Allows `std::unique_ptr` to use allocators (waiting for C++20). + * @tparam Type Type of object to allocate for and to construct. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A properly initialized unique pointer with a custom deleter. + */ +template +ENTT_CONSTEXPR auto allocate_unique(Allocator &allocator, Args &&...args) { + static_assert(!std::is_array_v, "Array types are not supported"); + + using alloc_traits = typename std::allocator_traits::template rebind_traits; + using allocator_type = typename alloc_traits::allocator_type; + + allocator_type alloc{allocator}; + auto ptr = alloc_traits::allocate(alloc, 1u); + + ENTT_TRY { + alloc_traits::construct(alloc, to_address(ptr), std::forward(args)...); + } + ENTT_CATCH { + alloc_traits::deallocate(alloc, ptr, 1u); + ENTT_THROW; + } + + return std::unique_ptr>{ptr, alloc}; +} + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct uses_allocator_construction { + template + static constexpr auto args([[maybe_unused]] const Allocator &allocator, Params &&...params) noexcept { + if constexpr(!std::uses_allocator_v && std::is_constructible_v) { + return std::forward_as_tuple(std::forward(params)...); + } else { + static_assert(std::uses_allocator_v, "Ill-formed request"); + + if constexpr(std::is_constructible_v) { + return std::tuple{std::allocator_arg, allocator, std::forward(params)...}; + } else { + static_assert(std::is_constructible_v, "Ill-formed request"); + return std::forward_as_tuple(std::forward(params)..., allocator); + } + } + } +}; + +template +struct uses_allocator_construction> { + using type = std::pair; + + template + static constexpr auto args(const Allocator &allocator, std::piecewise_construct_t, First &&first, Second &&second) noexcept { + return std::make_tuple( + std::piecewise_construct, + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(first)), + std::apply([&allocator](auto &&...curr) { return uses_allocator_construction::args(allocator, std::forward(curr)...); }, std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::tuple<>{}, std::tuple<>{}); + } + + template + static constexpr auto args(const Allocator &allocator, First &&first, Second &&second) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::forward(first)), std::forward_as_tuple(std::forward(second))); + } + + template + static constexpr auto args(const Allocator &allocator, const std::pair &value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(value.first), std::forward_as_tuple(value.second)); + } + + template + static constexpr auto args(const Allocator &allocator, std::pair &&value) noexcept { + return uses_allocator_construction::args(allocator, std::piecewise_construct, std::forward_as_tuple(std::move(value.first)), std::forward_as_tuple(std::move(value.second))); + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Prepares the argument list needed to + * create an object of a given type by means of uses-allocator construction. + * + * @tparam Type Type to return arguments for. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return The arguments needed to create an object of the given type. + */ +template +constexpr auto uses_allocator_construction_args(const Allocator &allocator, Args &&...args) noexcept { + return internal::uses_allocator_construction::args(allocator, std::forward(args)...); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A newly created object of the given type. + */ +template +constexpr Type make_obj_using_allocator(const Allocator &allocator, Args &&...args) { + return std::make_from_tuple(internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +/** + * @brief Uses-allocator construction utility (waiting for C++20). + * + * Primarily intended for internal use. Creates an object of a given type by + * means of uses-allocator construction at an uninitialized memory location. + * + * @tparam Type Type of object to create. + * @tparam Allocator Type of allocator used to manage memory and elements. + * @tparam Args Types of arguments to use to construct the object. + * @param value Memory location in which to place the object. + * @param allocator The allocator to use. + * @param args Parameters to use to construct the object. + * @return A pointer to the newly created object of the given type. + */ +template +constexpr Type *uninitialized_construct_using_allocator(Type *value, const Allocator &allocator, Args &&...args) { + return std::apply([value](auto &&...curr) { return new(value) Type(std::forward(curr)...); }, internal::uses_allocator_construction::args(allocator, std::forward(args)...)); +} + +} // namespace entt + +#endif + +// #include "../core/type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + +// #include "fwd.hpp" +#ifndef ENTT_CONTAINER_FWD_HPP +#define ENTT_CONTAINER_FWD_HPP + +#include +#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 */ +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; + using iterator_concept = std::random_access_iterator_tag; + + constexpr dense_map_iterator() noexcept + : it{} {} + + constexpr dense_map_iterator(const It iter) noexcept + : it{iter} {} + + template && std::is_constructible_v>> + constexpr dense_map_iterator(const dense_map_iterator &other) noexcept + : it{other.it} {} + + constexpr dense_map_iterator &operator++() noexcept { + return ++it, *this; + } + + constexpr dense_map_iterator operator++(int) noexcept { + dense_map_iterator orig = *this; + return ++(*this), orig; + } + + constexpr dense_map_iterator &operator--() noexcept { + return --it, *this; + } + + constexpr dense_map_iterator operator--(int) noexcept { + dense_map_iterator orig = *this; + return operator--(), orig; + } + + constexpr dense_map_iterator &operator+=(const difference_type value) noexcept { + it += value; + return *this; + } + + constexpr dense_map_iterator operator+(const difference_type value) const noexcept { + dense_map_iterator copy = *this; + return (copy += value); + } + + constexpr dense_map_iterator &operator-=(const difference_type value) noexcept { + return (*this += -value); + } + + constexpr dense_map_iterator operator-(const difference_type value) const noexcept { + return (*this + -value); + } + + [[nodiscard]] constexpr reference operator[](const difference_type value) const noexcept { + return {it[value].element.first, it[value].element.second}; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it->element.first, it->element.second}; + } + + template + friend constexpr std::ptrdiff_t operator-(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator==(const dense_map_iterator &, const dense_map_iterator &) noexcept; + + template + friend constexpr bool operator<(const dense_map_iterator &, const dense_map_iterator &) noexcept; + +private: + It it; +}; + +template +[[nodiscard]] constexpr std::ptrdiff_t operator-(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it - rhs.it; +} + +template +[[nodiscard]] constexpr bool operator==(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it == rhs.it; +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +template +[[nodiscard]] constexpr bool operator<(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return lhs.it < rhs.it; +} + +template +[[nodiscard]] constexpr bool operator>(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return rhs < lhs; +} + +template +[[nodiscard]] constexpr bool operator<=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs > rhs); +} + +template +[[nodiscard]] constexpr bool operator>=(const dense_map_iterator &lhs, const dense_map_iterator &rhs) noexcept { + return !(lhs < rhs); +} + +template +class dense_map_local_iterator final { + template + friend class dense_map_local_iterator; + + using first_type = decltype(std::as_const(std::declval()->element.first)); + using second_type = decltype((std::declval()->element.second)); + +public: + using value_type = std::pair; + using pointer = input_iterator_pointer; + using reference = value_type; + using difference_type = std::ptrdiff_t; + using iterator_category = std::input_iterator_tag; + using iterator_concept = std::forward_iterator_tag; + + constexpr dense_map_local_iterator() noexcept + : it{}, + offset{} {} + + constexpr dense_map_local_iterator(It iter, const std::size_t pos) noexcept + : it{iter}, + offset{pos} {} + + template && std::is_constructible_v>> + constexpr dense_map_local_iterator(const dense_map_local_iterator &other) noexcept + : it{other.it}, + offset{other.offset} {} + + constexpr dense_map_local_iterator &operator++() noexcept { + return offset = it[offset].next, *this; + } + + constexpr dense_map_local_iterator operator++(int) noexcept { + dense_map_local_iterator orig = *this; + return ++(*this), orig; + } + + [[nodiscard]] constexpr pointer operator->() const noexcept { + return operator*(); + } + + [[nodiscard]] constexpr reference operator*() const noexcept { + return {it[offset].element.first, it[offset].element.second}; + } + + [[nodiscard]] constexpr std::size_t index() const noexcept { + return offset; + } + +private: + It it; + std::size_t offset; +}; + +template +[[nodiscard]] constexpr bool operator==(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return lhs.index() == rhs.index(); +} + +template +[[nodiscard]] constexpr bool operator!=(const dense_map_local_iterator &lhs, const dense_map_local_iterator &rhs) noexcept { + return !(lhs == rhs); +} + +} // namespace internal +/*! @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 = std::allocator_traits; + static_assert(std::is_same_v>, "Invalid value type"); + using sparse_container_type = std::vector>; + using packed_container_type = std::vector>; + + template + [[nodiscard]] std::size_t key_to_bucket(const Other &key) const noexcept { + return fast_mod(static_cast(sparse.second()(key)), bucket_count()); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) { + for(auto it = begin(bucket), last = end(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return begin() + static_cast(it.index()); + } + } + + return end(); + } + + template + [[nodiscard]] auto constrained_find(const Other &key, std::size_t bucket) const { + for(auto it = cbegin(bucket), last = cend(bucket); it != last; ++it) { + if(packed.second()(it->first, key)) { + return cbegin() + static_cast(it.index()); + } + } + + return cend(); + } + + template + [[nodiscard]] auto insert_or_do_nothing(Other &&key, Args &&...args) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::piecewise_construct, std::forward_as_tuple(std::forward(key)), std::forward_as_tuple(std::forward(args)...)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + template + [[nodiscard]] auto insert_or_overwrite(Other &&key, Arg &&value) { + const auto index = key_to_bucket(key); + + if(auto it = constrained_find(key, index); it != end()) { + it->second = std::forward(value); + return std::make_pair(it, false); + } + + packed.first().emplace_back(sparse.first()[index], std::forward(key), std::forward(value)); + sparse.first()[index] = packed.first().size() - 1u; + rehash_if_required(); + + return std::make_pair(--end(), true); + } + + void move_and_pop(const std::size_t pos) { + if(const auto last = size() - 1u; pos != last) { + size_type *curr = sparse.first().data() + key_to_bucket(packed.first().back().element.first); + packed.first()[pos] = std::move(packed.first().back()); + for(; *curr != last; curr = &packed.first()[*curr].next) {} + *curr = pos; + } + + packed.first().pop_back(); + } + + void rehash_if_required() { + if(size() > (bucket_count() * max_load_factor())) { + rehash(bucket_count() * 2u); + } + } + +public: + /*! @brief Key type of the container. */ + using key_type = Key; + /*! @brief Mapped type of the container. */ + using mapped_type = Type; + /*! @brief Key-value type of the container. */ + using value_type = std::pair; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Type of function to use to hash the keys. */ + using hasher = Hash; + /*! @brief Type of function to use to compare the keys for equality. */ + using key_equal = KeyEqual; + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Input iterator type. */ + using iterator = internal::dense_map_iterator; + /*! @brief Constant input iterator type. */ + using const_iterator = internal::dense_map_iterator; + /*! @brief Input iterator type. */ + using local_iterator = internal::dense_map_local_iterator; + /*! @brief Constant input iterator type. */ + using const_local_iterator = internal::dense_map_local_iterator; + + /*! @brief Default constructor. */ + dense_map() + : dense_map{minimum_capacity} {} + + /** + * @brief Constructs an empty container with a given allocator. + * @param allocator The allocator to use. + */ + explicit dense_map(const allocator_type &allocator) + : dense_map{minimum_capacity, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator and user + * supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const allocator_type &allocator) + : dense_map{cnt, hasher{}, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param allocator The allocator to use. + */ + dense_map(const size_type cnt, const hasher &hash, const allocator_type &allocator) + : dense_map{cnt, hash, key_equal{}, allocator} {} + + /** + * @brief Constructs an empty container with a given allocator, hash + * function, compare function and user supplied minimal number of buckets. + * @param cnt Minimal number of buckets. + * @param hash Hash function to use. + * @param equal Compare function to use. + * @param allocator The allocator to use. + */ + explicit dense_map(const size_type cnt, const hasher &hash = hasher{}, const key_equal &equal = key_equal{}, const allocator_type &allocator = allocator_type{}) + : sparse{allocator, hash}, + packed{allocator, equal}, + threshold{default_threshold} { + rehash(cnt); + } + + /*! @brief Default copy constructor. */ + dense_map(const dense_map &) = default; + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + dense_map(const dense_map &other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(other.sparse.first(), allocator), std::forward_as_tuple(other.sparse.second())}, + packed{std::piecewise_construct, std::forward_as_tuple(other.packed.first(), allocator), std::forward_as_tuple(other.packed.second())}, + threshold{other.threshold} {} + + /*! @brief Default move constructor. */ + dense_map(dense_map &&) noexcept(std::is_nothrow_move_constructible_v> &&std::is_nothrow_move_constructible_v>) = default; + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + dense_map(dense_map &&other, const allocator_type &allocator) + : sparse{std::piecewise_construct, std::forward_as_tuple(std::move(other.sparse.first()), allocator), std::forward_as_tuple(std::move(other.sparse.second()))}, + packed{std::piecewise_construct, std::forward_as_tuple(std::move(other.packed.first()), allocator), std::forward_as_tuple(std::move(other.packed.second()))}, + threshold{other.threshold} {} + + /** + * @brief Default copy assignment operator. + * @return This container. + */ + dense_map &operator=(const dense_map &) = default; + + /** + * @brief Default move assignment operator. + * @return This container. + */ + dense_map &operator=(dense_map &&) noexcept(std::is_nothrow_move_assignable_v> &&std::is_nothrow_move_assignable_v>) = default; + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return sparse.first().get_allocator(); + } + + /** + * @brief Returns an iterator to the beginning. + * + * If the array is empty, the returned iterator will be equal to `end()`. + * + * @return An iterator to the first instance of the internal array. + */ + [[nodiscard]] const_iterator cbegin() const noexcept { + return packed.first().begin(); + } + + /*! @copydoc cbegin */ + [[nodiscard]] const_iterator begin() const noexcept { + return cbegin(); + } + + /*! @copydoc begin */ + [[nodiscard]] iterator begin() noexcept { + return packed.first().begin(); + } + + /** + * @brief Returns an iterator to the end. + * @return An iterator to the element following the last instance of the + * internal array. + */ + [[nodiscard]] const_iterator cend() const noexcept { + return packed.first().end(); + } + + /*! @copydoc cend */ + [[nodiscard]] const_iterator end() const noexcept { + return cend(); + } + + /*! @copydoc end */ + [[nodiscard]] iterator end() noexcept { + return packed.first().end(); + } + + /** + * @brief Checks whether a container is empty. + * @return True if the container is empty, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return packed.first().empty(); + } + + /** + * @brief Returns the number of elements in a container. + * @return Number of elements in a container. + */ + [[nodiscard]] size_type size() const noexcept { + return packed.first().size(); + } + + /** + * @brief Returns the maximum possible number of elements. + * @return Maximum possible number of elements. + */ + [[nodiscard]] size_type max_size() const noexcept { + return packed.first().max_size(); + } + + /*! @brief Clears the container. */ + void clear() noexcept { + sparse.first().clear(); + packed.first().clear(); + rehash(0u); + } + + /** + * @brief Inserts an element into the container, if the key does not exist. + * @param value A key-value pair eventually convertible to the value type. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + std::pair insert(const value_type &value) { + return insert_or_do_nothing(value.first, value.second); + } + + /*! @copydoc insert */ + std::pair insert(value_type &&value) { + return insert_or_do_nothing(std::move(value.first), std::move(value.second)); + } + + /** + * @copydoc insert + * @tparam Arg Type of the key-value pair to insert into the container. + */ + template + std::enable_if_t, std::pair> + insert(Arg &&value) { + return insert_or_do_nothing(std::forward(value).first, std::forward(value).second); + } + + /** + * @brief Inserts elements into the container, if their keys do not exist. + * @tparam It Type of input iterator. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + */ + template + void insert(It first, It last) { + for(; first != last; ++first) { + insert(*first); + } + } + + /** + * @brief Inserts an element into the container or assigns to the current + * element if the key already exists. + * @tparam Arg Type of the value to insert or assign. + * @param key A key used both to look up and to insert if not found. + * @param value A value to insert or assign. + * @return A pair consisting of an iterator to the element and a bool + * denoting whether the insertion took place. + */ + template + std::pair insert_or_assign(const key_type &key, Arg &&value) { + return insert_or_overwrite(key, std::forward(value)); + } + + /*! @copydoc insert_or_assign */ + template + std::pair insert_or_assign(key_type &&key, Arg &&value) { + return insert_or_overwrite(std::move(key), std::forward(value)); + } + + /** + * @brief Constructs an element in-place, if the key does not exist. + * + * The element is also constructed when the container already has the key, + * in which case the newly constructed object is destroyed immediately. + * + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair emplace([[maybe_unused]] Args &&...args) { + if constexpr(sizeof...(Args) == 0u) { + return insert_or_do_nothing(key_type{}); + } else if constexpr(sizeof...(Args) == 1u) { + return insert_or_do_nothing(std::forward(args).first..., std::forward(args).second...); + } else if constexpr(sizeof...(Args) == 2u) { + return insert_or_do_nothing(std::forward(args)...); + } else { + auto &node = packed.first().emplace_back(packed.first().size(), std::forward(args)...); + const auto index = key_to_bucket(node.element.first); + + if(auto it = constrained_find(node.element.first, index); it != end()) { + packed.first().pop_back(); + return std::make_pair(it, false); + } + + std::swap(node.next, sparse.first()[index]); + rehash_if_required(); + + return std::make_pair(--end(), true); + } + } + + /** + * @brief Inserts in-place if the key does not exist, does nothing if the + * key exists. + * @tparam Args Types of arguments to forward to the constructor of the + * element. + * @param key A key used both to look up and to insert if not found. + * @param args Arguments to forward to the constructor of the element. + * @return A pair consisting of an iterator to the inserted element (or to + * the element that prevented the insertion) and a bool denoting whether the + * insertion took place. + */ + template + std::pair try_emplace(const key_type &key, Args &&...args) { + return insert_or_do_nothing(key, std::forward(args)...); + } + + /*! @copydoc try_emplace */ + template + std::pair try_emplace(key_type &&key, Args &&...args) { + return insert_or_do_nothing(std::move(key), std::forward(args)...); + } + + /** + * @brief Removes an element from a given position. + * @param pos An iterator to the element to remove. + * @return An iterator following the removed element. + */ + iterator erase(const_iterator pos) { + const auto diff = pos - cbegin(); + erase(pos->first); + return begin() + diff; + } + + /** + * @brief Removes the given elements from a container. + * @param first An iterator to the first element of the range of elements. + * @param last An iterator past the last element of the range of elements. + * @return An iterator following the last removed element. + */ + iterator erase(const_iterator first, const_iterator last) { + const auto dist = first - cbegin(); + + for(auto from = last - cbegin(); from != dist; --from) { + erase(packed.first()[from - 1u].element.first); + } + + return (begin() + dist); + } + + /** + * @brief Removes the element associated with a given key. + * @param key A key value of an element to remove. + * @return Number of elements removed (either 0 or 1). + */ + size_type erase(const key_type &key) { + for(size_type *curr = sparse.first().data() + key_to_bucket(key); *curr != (std::numeric_limits::max)(); curr = &packed.first()[*curr].next) { + if(packed.second()(packed.first()[*curr].element.first, key)) { + const auto index = *curr; + *curr = packed.first()[*curr].next; + move_and_pop(index); + return 1u; + } + } + + return 0u; + } + + /** + * @brief Exchanges the contents with those of a given container. + * @param other Container to exchange the content with. + */ + void swap(dense_map &other) { + using std::swap; + swap(sparse, other.sparse); + swap(packed, other.packed); + swap(threshold, other.threshold); + } + + /** + * @brief Accesses a given element with bounds checking. + * @param key A key of an element to find. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &at(const key_type &key) { + auto it = find(key); + ENTT_ASSERT(it != end(), "Invalid key"); + return it->second; + } + + /*! @copydoc at */ + [[nodiscard]] const mapped_type &at(const key_type &key) const { + auto it = find(key); + ENTT_ASSERT(it != cend(), "Invalid key"); + return it->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](const key_type &key) { + return insert_or_do_nothing(key).first->second; + } + + /** + * @brief Accesses or inserts a given element. + * @param key A key of an element to find or insert. + * @return A reference to the mapped value of the requested element. + */ + [[nodiscard]] mapped_type &operator[](key_type &&key) { + return insert_or_do_nothing(std::move(key)).first->second; + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + [[nodiscard]] size_type count(const key_type &key) const { + return find(key) != end(); + } + + /** + * @brief Returns the number of elements matching a key (either 1 or 0). + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return Number of elements matching the key (either 1 or 0). + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + count(const Other &key) const { + return find(key) != end(); + } + + /** + * @brief Finds an element with a given key. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + [[nodiscard]] iterator find(const key_type &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + [[nodiscard]] const_iterator find(const key_type &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Finds an element with a key that compares _equivalent_ to a given + * key. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return An iterator to an element with the given key. If no such element + * is found, a past-the-end iterator is returned. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) { + return constrained_find(key, key_to_bucket(key)); + } + + /*! @copydoc find */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + find(const Other &key) const { + return constrained_find(key, key_to_bucket(key)); + } + + /** + * @brief Returns a range containing all elements with a given key. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + [[nodiscard]] std::pair equal_range(const key_type &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + [[nodiscard]] std::pair equal_range(const key_type &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Returns a range containing all elements that compare _equivalent_ + * to a given key. + * @tparam Other Type of an element to search for. + * @param key Key value of an element to search for. + * @return A pair of iterators pointing to the first element and past the + * last element of the range. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) { + const auto it = find(key); + return {it, it + !(it == end())}; + } + + /*! @copydoc equal_range */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t>> + equal_range(const Other &key) const { + const auto it = find(key); + return {it, it + !(it == cend())}; + } + + /** + * @brief Checks if the container contains an element with a given key. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + [[nodiscard]] bool contains(const key_type &key) const { + return (find(key) != cend()); + } + + /** + * @brief Checks if the container contains an element with a key that + * compares _equivalent_ to a given value. + * @tparam Other Type of the key value of an element to search for. + * @param key Key value of an element to search for. + * @return True if there is such an element, false otherwise. + */ + template + [[nodiscard]] std::enable_if_t && is_transparent_v, std::conditional_t> + contains(const Other &key) const { + return (find(key) != cend()); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator cbegin(const size_type index) const { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] const_local_iterator begin(const size_type index) const { + return cbegin(index); + } + + /** + * @brief Returns an iterator to the beginning of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the beginning of the given bucket. + */ + [[nodiscard]] local_iterator begin(const size_type index) { + return {packed.first().begin(), sparse.first()[index]}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator cend([[maybe_unused]] const size_type index) const { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] const_local_iterator end(const size_type index) const { + return cend(index); + } + + /** + * @brief Returns an iterator to the end of a given bucket. + * @param index An index of a bucket to access. + * @return An iterator to the end of the given bucket. + */ + [[nodiscard]] local_iterator end([[maybe_unused]] const size_type index) { + return {packed.first().begin(), (std::numeric_limits::max)()}; + } + + /** + * @brief Returns the number of buckets. + * @return The number of buckets. + */ + [[nodiscard]] size_type bucket_count() const { + return sparse.first().size(); + } + + /** + * @brief Returns the maximum number of buckets. + * @return The maximum number of buckets. + */ + [[nodiscard]] size_type max_bucket_count() const { + return sparse.first().max_size(); + } + + /** + * @brief Returns the number of elements in a given bucket. + * @param index The index of the bucket to examine. + * @return The number of elements in the given bucket. + */ + [[nodiscard]] size_type bucket_size(const size_type index) const { + return static_cast(std::distance(begin(index), end(index))); + } + + /** + * @brief Returns the bucket for a given key. + * @param key The value of the key to examine. + * @return The bucket for the given key. + */ + [[nodiscard]] size_type bucket(const key_type &key) const { + return key_to_bucket(key); + } + + /** + * @brief Returns the average number of elements per bucket. + * @return The average number of elements per bucket. + */ + [[nodiscard]] float load_factor() const { + return size() / static_cast(bucket_count()); + } + + /** + * @brief Returns the maximum average number of elements per bucket. + * @return The maximum average number of elements per bucket. + */ + [[nodiscard]] float max_load_factor() const { + return threshold; + } + + /** + * @brief Sets the desired maximum average number of elements per bucket. + * @param value A desired maximum average number of elements per bucket. + */ + void max_load_factor(const float value) { + ENTT_ASSERT(value > 0.f, "Invalid load factor"); + threshold = value; + rehash(0u); + } + + /** + * @brief Reserves at least the specified number of buckets and regenerates + * the hash table. + * @param cnt New number of buckets. + */ + void rehash(const size_type cnt) { + auto value = cnt > minimum_capacity ? cnt : minimum_capacity; + const auto cap = static_cast(size() / max_load_factor()); + value = value > cap ? value : cap; + + if(const auto sz = next_power_of_two(value); sz != bucket_count()) { + sparse.first().resize(sz); + + for(auto &&elem: sparse.first()) { + elem = (std::numeric_limits::max)(); + } + + for(size_type pos{}, last = size(); pos < last; ++pos) { + const auto index = key_to_bucket(packed.first()[pos].element.first); + packed.first()[pos].next = std::exchange(sparse.first()[index], pos); + } + } + } + + /** + * @brief Reserves space for at least the specified number of elements and + * regenerates the hash table. + * @param cnt New number of elements. + */ + void reserve(const size_type cnt) { + packed.first().reserve(cnt); + rehash(static_cast(std::ceil(cnt / max_load_factor()))); + } + + /** + * @brief Returns the function used to hash the keys. + * @return The function used to hash the keys. + */ + [[nodiscard]] hasher hash_function() const { + return sparse.second(); + } + + /** + * @brief Returns the function used to compare keys for equality. + * @return The function used to compare keys for equality. + */ + [[nodiscard]] key_equal key_eq() const { + return packed.second(); + } + +private: + compressed_pair sparse; + compressed_pair packed; + float threshold; +}; + +} // namespace entt + +/*! @cond TURN_OFF_DOXYGEN */ +namespace std { + +template +struct uses_allocator, Allocator> + : std::true_type {}; + +} // namespace std +/*! @endcond */ + +#endif + +// #include "../core/compressed_pair.hpp" +#ifndef ENTT_CORE_COMPRESSED_PAIR_HPP +#define ENTT_CORE_COMPRESSED_PAIR_HPP + +#include +#include +#include +#include +// #include "type_traits.hpp" +#ifndef ENTT_CORE_TYPE_TRAITS_HPP +#define ENTT_CORE_TYPE_TRAITS_HPP + +#include +#include +#include +#include +#include +// #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. + */ +template +struct size_of: std::integral_constant {}; + +/*! @copydoc size_of */ +template +struct size_of> + : std::integral_constant {}; + +/** + * @brief Helper variable template. + * @tparam Type The type of which to return the size. + */ +template +inline constexpr std::size_t size_of_v = size_of::value; + +/** + * @brief Using declaration to be used to _repeat_ the same type a number of + * times equal to the size of a given parameter pack. + * @tparam Type A type to repeat. + */ +template +using unpack_as_type = Type; + +/** + * @brief Helper variable template to be used to _repeat_ the same value a + * number of times equal to the size of a given parameter pack. + * @tparam Value A value to repeat. + */ +template +inline constexpr auto unpack_as_value = Value; + +/** + * @brief Wraps a static constant. + * @tparam Value A static constant. + */ +template +using integral_constant = std::integral_constant; + +/** + * @brief Alias template to facilitate the creation of named values. + * @tparam Value A constant value at least convertible to `id_type`. + */ +template +using tag = integral_constant; + +/** + * @brief A class to use to push around lists of types, nothing more. + * @tparam Type Types provided by the type list. + */ +template +struct type_list { + /*! @brief Type list type. */ + using type = type_list; + /*! @brief Compile-time number of elements in the type list. */ + static constexpr auto size = sizeof...(Type); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_element; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Index Index of the type to return. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element> + : type_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_element<0u, type_list> { + /*! @brief Searched type. */ + using type = First; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Type list to search into. + */ +template +using type_list_element_t = typename type_list_element::type; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_index; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam First First type provided by the type list. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 1u + type_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + * @tparam Other Other types provided by the type list. + */ +template +struct type_list_index> { + static_assert(type_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the types of a type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +struct type_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for and for which to return the index. + */ +template +inline constexpr std::size_t type_list_index_v = type_list_index::value; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @return A type list composed by the types of both the type lists. + */ +template +constexpr type_list operator+(type_list, type_list) { + return {}; +} + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_cat; + +/*! @brief Concatenates multiple type lists. */ +template<> +struct type_list_cat<> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list<>; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + * @tparam List Other type lists, if any. + */ +template +struct type_list_cat, type_list, List...> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = typename type_list_cat, List...>::type; +}; + +/** + * @brief Concatenates multiple type lists. + * @tparam Type Types provided by the type list. + */ +template +struct type_list_cat> { + /*! @brief A type list composed by the types of all the type lists. */ + using type = type_list; +}; + +/** + * @brief Helper type. + * @tparam List Type lists to concatenate. + */ +template +using type_list_cat_t = typename type_list_cat::type; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct type_list_unique; + +template +struct type_list_unique, Type...> + : std::conditional_t<(std::is_same_v || ...), type_list_unique, Type...>, type_list_unique, Type..., First>> {}; + +template +struct type_list_unique, Type...> { + using type = type_list; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Removes duplicates types from a type list. + * @tparam List Type list. + */ +template +struct type_list_unique { + /*! @brief A type list without duplicate types. */ + using type = typename internal::type_list_unique::type; +}; + +/** + * @brief Helper type. + * @tparam List 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::bool_constant<(std::is_same_v || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Type list. + * @tparam Type Type to look for. + */ +template +inline constexpr bool type_list_contains_v = type_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct type_list_diff; + +/** + * @brief Computes the difference between two type lists. + * @tparam Type Types provided by the first type list. + * @tparam Other Types provided by the second type list. + */ +template +struct type_list_diff, type_list> { + /*! @brief A type list that is the difference between the two type lists. */ + using type = type_list_cat_t, Type>, type_list<>, type_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Type lists between which to compute the difference. + */ +template +using type_list_diff_t = typename type_list_diff::type; + +/*! @brief Primary template isn't defined on purpose. */ +template class> +struct type_list_transform; + +/** + * @brief Applies a given _function_ to a type list and generate a new list. + * @tparam Type Types provided by the type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +struct type_list_transform, Op> { + /*! @brief Resulting type list after applying the transform function. */ + using type = type_list::type...>; +}; + +/** + * @brief Helper type. + * @tparam List Type list. + * @tparam Op Unary operation as template class with a type member named `type`. + */ +template class Op> +using type_list_transform_t = typename type_list_transform::type; + +/** + * @brief A class to use to push around lists of constant values, nothing more. + * @tparam Value Values provided by the value list. + */ +template +struct value_list { + /*! @brief Value list type. */ + using type = value_list; + /*! @brief Compile-time number of elements in the value list. */ + static constexpr auto size = sizeof...(Value); +}; + +/*! @brief Primary template isn't defined on purpose. */ +template +struct value_list_element; + +/** + * @brief Provides compile-time indexed access to the values of a value list. + * @tparam Index Index of the value to return. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element> + : value_list_element> {}; + +/** + * @brief Provides compile-time indexed access to the types of a type list. + * @tparam Value First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_element<0u, value_list> { + /*! @brief Searched type. */ + using type = decltype(Value); + /*! @brief Searched value. */ + static constexpr auto value = Value; +}; + +/** + * @brief Helper type. + * @tparam Index Index of the type to return. + * @tparam List Value list to search into. + */ +template +using value_list_element_t = typename value_list_element::type; + +/** + * @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 Primary template isn't defined on purpose. */ +template +struct value_list_index; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam First First value provided by the value list. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 1u + value_list_index>::value; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + * @tparam Other Other values provided by the value list. + */ +template +struct value_list_index> { + static_assert(value_list_index>::value == sizeof...(Other), "Non-unique type"); + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given value in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Provides compile-time type access to the values of a value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +struct value_list_index> { + /*! @brief Unsigned integer type. */ + using value_type = std::size_t; + /*! @brief Compile-time position of the given type in the sublist. */ + static constexpr value_type value = 0u; +}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for and for which to return the index. + */ +template +inline constexpr std::size_t value_list_index_v = value_list_index::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 Primary template isn't defined on purpose. */ +template +struct value_list_unique; + +/** + * @brief Removes duplicates values from a value list. + * @tparam Value One of the values provided by the given value list. + * @tparam Other The other values provided by the given value list. + */ +template +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = std::conditional_t< + ((Value == Other) || ...), + typename value_list_unique>::type, + value_list_cat_t, typename value_list_unique>::type>>; +}; + +/*! @brief Removes duplicates values from a value list. */ +template<> +struct value_list_unique> { + /*! @brief A value list without duplicate types. */ + using type = value_list<>; +}; + +/** + * @brief Helper type. + * @tparam Type A value list. + */ +template +using value_list_unique_t = typename value_list_unique::type; + +/** + * @brief Provides the member constant `value` to true if a value list contains + * a given value, false otherwise. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +struct value_list_contains; + +/** + * @copybrief value_list_contains + * @tparam Value Values provided by the value list. + * @tparam Other Value to look for. + */ +template +struct value_list_contains, Other> + : std::bool_constant<((Value == Other) || ...)> {}; + +/** + * @brief Helper variable template. + * @tparam List Value list. + * @tparam Value Value to look for. + */ +template +inline constexpr bool value_list_contains_v = value_list_contains::value; + +/*! @brief Primary template isn't defined on purpose. */ +template +class value_list_diff; + +/** + * @brief Computes the difference between two value lists. + * @tparam Value Values provided by the first value list. + * @tparam Other Values provided by the second value list. + */ +template +class value_list_diff, value_list> { + using v141_toolset_workaround = value_list; + +public: + /*! @brief A value list that is the difference between the two value lists. */ + using type = value_list_cat_t, value_list<>, value_list>...>; +}; + +/** + * @brief Helper type. + * @tparam List Value lists between which to compute the difference. + */ +template +using value_list_diff_t = typename value_list_diff::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 */ +namespace internal { + +template +struct has_iterator_category: std::false_type {}; + +template +struct has_iterator_category::iterator_category>>: std::true_type {}; + +} // namespace internal +/*! @endcond */ + +/*! @copydoc is_iterator */ +template +struct is_iterator>>>> + : 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::bool_constant && !std::is_final_v> {}; + +/** + * @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; + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct has_tuple_size_value: std::false_type {}; + +template +struct has_tuple_size_value::value)>>: std::true_type {}; + +template +struct has_value_type: std::false_type {}; + +template +struct has_value_type>: std::true_type {}; + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable(); + +template +[[nodiscard]] constexpr bool unpack_maybe_equality_comparable(std::index_sequence) { + return (dispatch_is_equality_comparable>() && ...); +} + +template +[[nodiscard]] constexpr bool maybe_equality_comparable(char) { + return false; +} + +template +[[nodiscard]] constexpr auto maybe_equality_comparable(int) -> decltype(std::declval() == std::declval()) { + return true; +} + +template +[[nodiscard]] constexpr bool dispatch_is_equality_comparable() { + if constexpr(std::is_array_v) { + return false; + } else if constexpr(is_iterator_v) { + return maybe_equality_comparable(0); + } else if constexpr(has_value_type::value) { + if constexpr(std::is_same_v) { + return maybe_equality_comparable(0); + } else if constexpr(dispatch_is_equality_comparable()) { + return maybe_equality_comparable(0); + } else { + return false; + } + } else if constexpr(is_complete_v>>) { + if constexpr(has_tuple_size_value::value) { + return maybe_equality_comparable(0) && unpack_maybe_equality_comparable(std::make_index_sequence::value>{}); + } else { + return maybe_equality_comparable(0); + } + } else { + return maybe_equality_comparable(0); + } +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Provides the member constant `value` to true if a given type is + * equality comparable, false otherwise. + * @tparam Type The type to test. + */ +template +struct is_equality_comparable: std::bool_constant()> {}; + +/*! @copydoc is_equality_comparable */ +template +struct is_equality_comparable: is_equality_comparable {}; + +/** + * @brief Helper variable template. + * @tparam Type The type to test. + */ +template +inline constexpr bool is_equality_comparable_v = is_equality_comparable::value; + +/** + * @brief Transcribes the constness of a type to another type. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = std::remove_const_t; +}; + +/*! @copydoc constness_as */ +template +struct constness_as { + /*! @brief The type resulting from the transcription of the constness. */ + using type = const To; +}; + +/** + * @brief Alias template to facilitate the transcription of the constness. + * @tparam To The type to which to transcribe the constness. + * @tparam From The type from which to transcribe the constness. + */ +template +using constness_as_t = typename constness_as::type; + +/** + * @brief Extracts the class of a non-static member object or function. + * @tparam Member A pointer to a non-static member object or function. + */ +template +class member_class { + static_assert(std::is_member_pointer_v, "Invalid pointer type to non-static member object or function"); + + template + static Class *clazz(Ret (Class::*)(Args...)); + + template + static Class *clazz(Ret (Class::*)(Args...) const); + + template + static Class *clazz(Type Class::*); + +public: + /*! @brief The class of the given non-static member object or function. */ + using type = std::remove_pointer_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Member A pointer to a non-static member object or function. + */ +template +using member_class_t = typename member_class::type; + +/** + * @brief Extracts the n-th argument of a given function or member function. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +class nth_argument { + template + static constexpr type_list pick_up(Ret (*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...)); + + template + static constexpr type_list pick_up(Ret (Class ::*)(Args...) const); + + template + static constexpr type_list pick_up(Type Class ::*); + +public: + /*! @brief N-th argument of the given function or member function. */ + using type = type_list_element_t()))>; +}; + +/** + * @brief Helper type. + * @tparam Index The index of the argument to extract. + * @tparam Candidate A valid function, member function or data member type. + */ +template +using nth_argument_t = typename nth_argument::type; + +} // namespace entt + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::type_list_element> {}; + +template +struct std::tuple_size>: std::integral_constant::size> {}; + +template +struct std::tuple_element>: entt::value_list_element> {}; + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +struct compressed_pair_element { + using reference = Type &; + using const_reference = const Type &; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : value{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : value{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return value; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return value; + } + +private: + Type value; +}; + +template +struct compressed_pair_element>>: Type { + using reference = Type &; + using const_reference = const Type &; + using base_type = Type; + + template>> + constexpr compressed_pair_element() noexcept(std::is_nothrow_default_constructible_v) + : base_type{} {} + + template>, compressed_pair_element>>> + constexpr compressed_pair_element(Arg &&arg) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(arg)} {} + + template + constexpr compressed_pair_element(std::tuple args, std::index_sequence) noexcept(std::is_nothrow_constructible_v) + : base_type{std::forward(std::get(args))...} {} + + [[nodiscard]] constexpr reference get() noexcept { + return *this; + } + + [[nodiscard]] constexpr const_reference get() const noexcept { + return *this; + } +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief A compressed pair. + * + * A pair that exploits the _Empty Base Class Optimization_ (or _EBCO_) to + * reduce its final size to a minimum. + * + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +class compressed_pair final + : internal::compressed_pair_element, + internal::compressed_pair_element { + using first_base = internal::compressed_pair_element; + using second_base = internal::compressed_pair_element; + +public: + /*! @brief The type of the first element that the pair stores. */ + using first_type = First; + /*! @brief The type of the second element that the pair stores. */ + using second_type = Second; + + /** + * @brief Default constructor, conditionally enabled. + * + * This constructor is only available when the types that the pair stores + * are both at least default constructible. + * + * @tparam Dummy Dummy template parameter used for internal purposes. + */ + template && std::is_default_constructible_v>> + constexpr compressed_pair() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_default_constructible_v) + : first_base{}, + second_base{} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + constexpr compressed_pair(const compressed_pair &other) noexcept(std::is_nothrow_copy_constructible_v &&std::is_nothrow_copy_constructible_v) = default; + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + constexpr compressed_pair(compressed_pair &&other) noexcept(std::is_nothrow_move_constructible_v &&std::is_nothrow_move_constructible_v) = default; + + /** + * @brief Constructs a pair from its values. + * @tparam Arg Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + * @param arg Value to use to initialize the first element. + * @param other Value to use to initialize the second element. + */ + template + constexpr compressed_pair(Arg &&arg, Other &&other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::forward(arg)}, + second_base{std::forward(other)} {} + + /** + * @brief Constructs a pair by forwarding the arguments to its parts. + * @tparam Args Types of arguments to use to initialize the first element. + * @tparam Other Types of arguments to use to initialize the second element. + * @param args Arguments to use to initialize the first element. + * @param other Arguments to use to initialize the second element. + */ + template + constexpr compressed_pair(std::piecewise_construct_t, std::tuple args, std::tuple other) noexcept(std::is_nothrow_constructible_v &&std::is_nothrow_constructible_v) + : first_base{std::move(args), std::index_sequence_for{}}, + second_base{std::move(other), std::index_sequence_for{}} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(const compressed_pair &other) noexcept(std::is_nothrow_copy_assignable_v &&std::is_nothrow_copy_assignable_v) = default; + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This compressed pair object. + */ + constexpr compressed_pair &operator=(compressed_pair &&other) noexcept(std::is_nothrow_move_assignable_v &&std::is_nothrow_move_assignable_v) = default; + + /** + * @brief Returns the first element that a pair stores. + * @return The first element that a pair stores. + */ + [[nodiscard]] constexpr first_type &first() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc first */ + [[nodiscard]] constexpr const first_type &first() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Returns the second element that a pair stores. + * @return The second element that a pair stores. + */ + [[nodiscard]] constexpr second_type &second() noexcept { + return static_cast(*this).get(); + } + + /*! @copydoc second */ + [[nodiscard]] constexpr const second_type &second() const noexcept { + return static_cast(*this).get(); + } + + /** + * @brief Swaps two compressed pair objects. + * @param other The compressed pair to swap with. + */ + constexpr void swap(compressed_pair &other) noexcept(std::is_nothrow_swappable_v &&std::is_nothrow_swappable_v) { + using std::swap; + swap(first(), other.first()); + swap(second(), other.second()); + } + + /** + * @brief Extracts an element from the compressed pair. + * @tparam Index An integer value that is either 0 or 1. + * @return Returns a reference to the first element if `Index` is 0 and a + * reference to the second element if `Index` is 1. + */ + template + constexpr decltype(auto) get() noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } + + /*! @copydoc get */ + template + constexpr decltype(auto) get() const noexcept { + if constexpr(Index == 0u) { + return first(); + } else { + static_assert(Index == 1u, "Index out of bounds"); + return second(); + } + } +}; + +/** + * @brief Deduction guide. + * @tparam Type Type of value to use to initialize the first element. + * @tparam Other Type of value to use to initialize the second element. + */ +template +compressed_pair(Type &&, Other &&) -> compressed_pair, std::decay_t>; + +/** + * @brief Swaps two compressed pair objects. + * @tparam First The type of the first element that the pairs store. + * @tparam Second The type of the second element that the pairs store. + * @param lhs A valid compressed pair object. + * @param rhs A valid compressed pair object. + */ +template +inline constexpr void swap(compressed_pair &lhs, compressed_pair &rhs) { + lhs.swap(rhs); +} + +} // namespace entt + +// disable structured binding support for clang 6, it messes when specializing tuple_size +#if !defined __clang_major__ || __clang_major__ > 6 +namespace std { + +/** + * @brief `std::tuple_size` specialization for `compressed_pair`s. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_size>: integral_constant {}; + +/** + * @brief `std::tuple_element` specialization for `compressed_pair`s. + * @tparam Index The index of the type to return. + * @tparam First The type of the first element that the pair stores. + * @tparam Second The type of the second element that the pair stores. + */ +template +struct tuple_element>: conditional { + static_assert(Index < 2u, "Index out of bounds"); +}; + +} // namespace std +#endif + +#endif + +// #include "../core/fwd.hpp" +#ifndef ENTT_CORE_FWD_HPP +#define ENTT_CORE_FWD_HPP + +#include +// #include "../config/config.h" + + +namespace entt { + +template +class basic_any; + +/*! @brief Alias declaration for type identifiers. */ +using id_type = ENTT_ID_TYPE; + +/*! @brief Alias declaration for the most common use case. */ +using any = basic_any<>; + +} // namespace entt + +#endif + +// #include "../core/type_info.hpp" +#ifndef ENTT_CORE_TYPE_INFO_HPP +#define ENTT_CORE_TYPE_INFO_HPP + +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/attribute.h" +#ifndef ENTT_CORE_ATTRIBUTE_H +#define ENTT_CORE_ATTRIBUTE_H + +#ifndef ENTT_EXPORT +# if defined _WIN32 || defined __CYGWIN__ || defined _MSC_VER +# define ENTT_EXPORT __declspec(dllexport) +# define ENTT_IMPORT __declspec(dllimport) +# define ENTT_HIDDEN +# elif defined __GNUC__ && __GNUC__ >= 4 +# define ENTT_EXPORT __attribute__((visibility("default"))) +# define ENTT_IMPORT __attribute__((visibility("default"))) +# define ENTT_HIDDEN __attribute__((visibility("hidden"))) +# else /* Unsupported compiler */ +# define ENTT_EXPORT +# define ENTT_IMPORT +# define ENTT_HIDDEN +# endif +#endif + +#ifndef ENTT_API +# if defined ENTT_API_EXPORT +# define ENTT_API ENTT_EXPORT +# elif defined ENTT_API_IMPORT +# define ENTT_API ENTT_IMPORT +# else /* No API */ +# define ENTT_API +# endif +#endif + +#endif + +// #include "fwd.hpp" + +// #include "hashed_string.hpp" +#ifndef ENTT_CORE_HASHED_STRING_HPP +#define ENTT_CORE_HASHED_STRING_HPP + +#include +#include +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +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 +/*! @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 traits_type = internal::fnv1a_traits; + + struct const_wrapper { + // non-explicit constructor on purpose + constexpr const_wrapper(const Char *str) noexcept + : repr{str} {} + + const Char *repr; + }; + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str) noexcept { + base_type base{str, 0u, traits_type::offset}; + + for(; str[base.length]; ++base.length) { + base.hash = (base.hash ^ static_cast(str[base.length])) * traits_type::prime; + } + + return base; + } + + // Fowler–Noll–Vo hash function v. 1a - the good + [[nodiscard]] static constexpr auto helper(const Char *str, const std::size_t len) noexcept { + base_type base{str, len, traits_type::offset}; + + for(size_type pos{}; pos < len; ++pos) { + base.hash = (base.hash ^ static_cast(str[pos])) * traits_type::prime; + } + + return base; + } + +public: + /*! @brief Character type. */ + using value_type = typename base_type::value_type; + /*! @brief Unsigned integer type. */ + using size_type = typename base_type::size_type; + /*! @brief Unsigned integer type. */ + using hash_type = typename base_type::hash_type; + + /** + * @brief Returns directly the numeric representation of a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const value_type *str, const size_type len) noexcept { + return basic_hashed_string{str, len}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + * @return The numeric representation of the string. + */ + template + [[nodiscard]] static constexpr hash_type value(const value_type (&str)[N]) noexcept { + return basic_hashed_string{str}; + } + + /** + * @brief Returns directly the numeric representation of a string. + * @param wrapper Helps achieving the purpose by relying on overloading. + * @return The numeric representation of the string. + */ + [[nodiscard]] static constexpr hash_type value(const_wrapper wrapper) noexcept { + return basic_hashed_string{wrapper}; + } + + /*! @brief Constructs an empty hashed string. */ + constexpr basic_hashed_string() noexcept + : base_type{} {} + + /** + * @brief Constructs a hashed string from a string view. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ + constexpr basic_hashed_string(const value_type *str, const size_type len) noexcept + : base_type{helper(str, len)} {} + + /** + * @brief Constructs a hashed string from an array of const characters. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ + template + constexpr basic_hashed_string(const value_type (&str)[N]) noexcept + : base_type{helper(str)} {} + + /** + * @brief Explicit constructor on purpose to avoid constructing a hashed + * string directly from a `const value_type *`. + * + * @warning + * The lifetime of the string is not extended nor is it copied. + * + * @param wrapper Helps achieving the purpose by relying on overloading. + */ + explicit constexpr basic_hashed_string(const_wrapper wrapper) noexcept + : base_type{helper(wrapper.repr)} {} + + /** + * @brief Returns the size a hashed string. + * @return The size of the hashed string. + */ + [[nodiscard]] constexpr size_type size() const noexcept { + return base_type::length; // NOLINT + } + + /** + * @brief Returns the human-readable representation of a hashed string. + * @return The string used to initialize the hashed string. + */ + [[nodiscard]] constexpr const value_type *data() const noexcept { + return base_type::repr; + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr hash_type value() const noexcept { + return base_type::hash; + } + + /*! @copydoc data */ + [[nodiscard]] constexpr operator const value_type *() const noexcept { + return data(); + } + + /** + * @brief Returns the numeric representation of a hashed string. + * @return The numeric representation of the hashed string. + */ + [[nodiscard]] constexpr operator hash_type() const noexcept { + return value(); + } +}; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @param str Human-readable identifier. + * @param len Length of the string to hash. + */ +template +basic_hashed_string(const Char *str, const std::size_t len) -> basic_hashed_string; + +/** + * @brief Deduction guide. + * @tparam Char Character type. + * @tparam N Number of characters of the identifier. + * @param str Human-readable identifier. + */ +template +basic_hashed_string(const Char (&str)[N]) -> basic_hashed_string; + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings are identical, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator==(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() == rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the two hashed strings differ, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator!=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than the second, false otherwise. + */ +template +[[nodiscard]] constexpr bool operator<(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return lhs.value() < rhs.value(); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator<=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than the second, false + * otherwise. + */ +template +[[nodiscard]] constexpr bool operator>(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two hashed strings. + * @tparam Char Character type. + * @param lhs A valid hashed string. + * @param rhs A valid hashed string. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +template +[[nodiscard]] constexpr bool operator>=(const basic_hashed_string &lhs, const basic_hashed_string &rhs) noexcept { + return !(lhs < rhs); +} + +/*! @brief Aliases for common character types. */ +using hashed_string = basic_hashed_string; + +/*! @brief Aliases for common character types. */ +using hashed_wstring = basic_hashed_string; + +inline namespace literals { + +/** + * @brief User defined literal for hashed strings. + * @param str The literal without its suffix. + * @return A properly initialized hashed string. + */ +[[nodiscard]] constexpr hashed_string operator"" _hs(const char *str, std::size_t) noexcept { + return hashed_string{str}; +} + +/** + * @brief User defined literal for hashed wstrings. + * @param str The literal without its suffix. + * @return A properly initialized hashed wstring. + */ +[[nodiscard]] constexpr hashed_wstring operator"" _hws(const wchar_t *str, std::size_t) noexcept { + return hashed_wstring{str}; +} + +} // namespace literals + +} // namespace entt + +#endif + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +struct ENTT_API type_index final { + [[nodiscard]] static id_type next() noexcept { + static ENTT_MAYBE_ATOMIC(id_type) value{}; + return value++; + } +}; + +template +[[nodiscard]] constexpr auto stripped_type_name() noexcept { +#if defined ENTT_PRETTY_FUNCTION + std::string_view pretty_function{ENTT_PRETTY_FUNCTION}; + auto first = pretty_function.find_first_not_of(' ', pretty_function.find_first_of(ENTT_PRETTY_FUNCTION_PREFIX) + 1); + auto value = pretty_function.substr(first, pretty_function.find_last_of(ENTT_PRETTY_FUNCTION_SUFFIX) - first); + return value; +#else + return std::string_view{""}; +#endif +} + +template().find_first_of('.')> +[[nodiscard]] constexpr std::string_view type_name(int) noexcept { + constexpr auto value = stripped_type_name(); + return value; +} + +template +[[nodiscard]] std::string_view type_name(char) noexcept { + static const auto value = stripped_type_name(); + return value; +} + +template().find_first_of('.')> +[[nodiscard]] constexpr id_type type_hash(int) noexcept { + constexpr auto stripped = stripped_type_name(); + constexpr auto value = hashed_string::value(stripped.data(), stripped.size()); + return value; +} + +template +[[nodiscard]] id_type type_hash(char) noexcept { + static const auto value = [](const auto stripped) { + return hashed_string::value(stripped.data(), stripped.size()); + }(stripped_type_name()); + return value; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Type sequential identifier. + * @tparam Type Type for which to generate a sequential identifier. + */ +template +struct ENTT_API type_index final { + /** + * @brief Returns the sequential identifier of a given type. + * @return The sequential identifier of a given type. + */ + [[nodiscard]] static id_type value() noexcept { + static const id_type value = internal::type_index::next(); + return value; + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type hash. + * @tparam Type Type for which to generate a hash value. + */ +template +struct type_hash final { + /** + * @brief Returns the numeric representation of a given type. + * @return The numeric representation of the given type. + */ +#if defined ENTT_PRETTY_FUNCTION + [[nodiscard]] static constexpr id_type value() noexcept { + return internal::type_hash(0); +#else + [[nodiscard]] static constexpr id_type value() noexcept { + return type_index::value(); +#endif + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator id_type() const noexcept { + return value(); + } +}; + +/** + * @brief Type name. + * @tparam Type Type for which to generate a name. + */ +template +struct type_name final { + /** + * @brief Returns the name of a given type. + * @return The name of the given type. + */ + [[nodiscard]] static constexpr std::string_view value() noexcept { + return internal::type_name(0); + } + + /*! @copydoc value */ + [[nodiscard]] constexpr operator std::string_view() const noexcept { + return value(); + } +}; + +/*! @brief Implementation specific information about a type. */ +struct type_info final { + /** + * @brief Constructs a type info object for a given type. + * @tparam Type Type for which to construct a type info object. + */ + template + constexpr type_info(std::in_place_type_t) noexcept + : seq{type_index>>::value()}, + identifier{type_hash>>::value()}, + alias{type_name>>::value()} {} + + /** + * @brief Type index. + * @return Type index. + */ + [[nodiscard]] constexpr id_type index() const noexcept { + return seq; + } + + /** + * @brief Type hash. + * @return Type hash. + */ + [[nodiscard]] constexpr id_type hash() const noexcept { + return identifier; + } + + /** + * @brief Type name. + * @return Type name. + */ + [[nodiscard]] constexpr std::string_view name() const noexcept { + return alias; + } + +private: + id_type seq; + id_type identifier; + std::string_view alias; +}; + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects are identical, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator==(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.hash() == rhs.hash(); +} + +/** + * @brief Compares the contents of two type info objects. + * @param lhs A type info object. + * @param rhs A type info object. + * @return True if the two type info objects differ, false otherwise. + */ +[[nodiscard]] inline constexpr bool operator!=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than the second, false otherwise. + */ +[[nodiscard]] constexpr bool operator<(const type_info &lhs, const type_info &rhs) noexcept { + return lhs.index() < rhs.index(); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is less than or equal to the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator<=(const type_info &lhs, const type_info &rhs) noexcept { + return !(rhs < lhs); +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than the second, false + * otherwise. + */ +[[nodiscard]] constexpr bool operator>(const type_info &lhs, const type_info &rhs) noexcept { + return rhs < lhs; +} + +/** + * @brief Compares two type info objects. + * @param lhs A valid type info object. + * @param rhs A valid type info object. + * @return True if the first element is greater than or equal to the second, + * false otherwise. + */ +[[nodiscard]] constexpr bool operator>=(const type_info &lhs, const type_info &rhs) noexcept { + return !(lhs < rhs); +} + +/** + * @brief Returns the type info object associated to a given type. + * + * The returned element refers to an object with static storage duration.
+ * The type doesn't need to be a complete type. If the type is a reference, the + * result refers to the referenced type. In all cases, top-level cv-qualifiers + * are ignored. + * + * @tparam Type Type for which to generate a type info object. + * @return A reference to a properly initialized type info object. + */ +template +[[nodiscard]] const type_info &type_id() noexcept { + if constexpr(std::is_same_v>>) { + static type_info instance{std::in_place_type}; + return instance; + } else { + return type_id>>(); + } +} + +/*! @copydoc type_id */ +template +[[nodiscard]] const type_info &type_id(Type &&) noexcept { + return type_id>>(); +} + +} // namespace entt + +#endif + +// #include "../core/utility.hpp" +#ifndef ENTT_CORE_UTILITY_HPP +#define ENTT_CORE_UTILITY_HPP + +#include +#include + +namespace entt { + +/*! @brief Identity function object (waiting for C++20). */ +struct identity { + /*! @brief Indicates that this is a transparent function object. */ + using is_transparent = void; + + /** + * @brief Returns its argument unchanged. + * @tparam Type Type of the argument. + * @param value The actual argument. + * @return The submitted value as-is. + */ + template + [[nodiscard]] constexpr Type &&operator()(Type &&value) const noexcept { + return std::forward(value); + } +}; + +/** + * @brief Constant utility to disambiguate overloaded members of a class. + * @tparam Type Type of the desired overload. + * @tparam Class Type of class to which the member belongs. + * @param member A valid pointer to a member. + * @return Pointer to the member. + */ +template +[[nodiscard]] constexpr auto overload(Type Class::*member) noexcept { + return member; +} + +/** + * @brief Constant utility to disambiguate overloaded functions. + * @tparam Func Function type of the desired overload. + * @param func A valid pointer to a function. + * @return Pointer to the function. + */ +template +[[nodiscard]] constexpr auto overload(Func *func) noexcept { + return func; +} + +/** + * @brief Helper type for visitors. + * @tparam Func Types of function objects. + */ +template +struct overloaded: Func... { + using Func::operator()...; +}; + +/** + * @brief Deduction guide. + * @tparam Func Types of function objects. + */ +template +overloaded(Func...) -> overloaded; + +/** + * @brief Basic implementation of a y-combinator. + * @tparam Func Type of a potentially recursive function. + */ +template +struct y_combinator { + /** + * @brief Constructs a y-combinator from a given function. + * @param recursive A potentially recursive function. + */ + constexpr y_combinator(Func recursive) noexcept(std::is_nothrow_move_constructible_v) + : func{std::move(recursive)} {} + + /** + * @brief Invokes a y-combinator and therefore its underlying function. + * @tparam Args Types of arguments to use to invoke the underlying function. + * @param args Parameters to use to invoke the underlying function. + * @return Return value of the underlying function, if any. + */ + template + constexpr decltype(auto) operator()(Args &&...args) const noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + + /*! @copydoc operator()() */ + template + constexpr decltype(auto) operator()(Args &&...args) noexcept(std::is_nothrow_invocable_v) { + return func(*this, std::forward(args)...); + } + +private: + Func func; +}; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + +// #include "sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" +#ifndef ENTT_SIGNAL_DELEGATE_HPP +#define ENTT_SIGNAL_DELEGATE_HPP + +#include +#include +#include +#include +#include +// #include "../config/config.h" + +// #include "../core/type_traits.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/*! @cond TURN_OFF_DOXYGEN */ +namespace internal { + +template +constexpr auto function_pointer(Ret (*)(Args...)) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (*)(Type, Args...), Other &&) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...), Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Ret (Class::*)(Args...) const, Other &&...) -> Ret (*)(Args...); + +template +constexpr auto function_pointer(Type Class::*, Other &&...) -> Type (*)(); + +template +using function_pointer_t = decltype(function_pointer(std::declval()...)); + +template +[[nodiscard]] constexpr auto index_sequence_for(Ret (*)(Args...)) { + return std::index_sequence_for{}; +} + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic delegate implementation. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + */ +template +class delegate; + +/** + * @brief Utility class to use to send around functions and members. + * + * Unmanaged delegate for function pointers and members. Users of this class are + * in charge of disconnecting instances before deleting them. + * + * A delegate can be used as a general purpose invoker without memory overhead + * for free functions possibly with payloads and bound or unbound members. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +class delegate { + template + [[nodiscard]] auto wrap(std::index_sequence) noexcept { + return [](const void *, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type &, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, *curr, std::forward>>(std::get(arguments))...)); + } + }; + } + + template + [[nodiscard]] auto wrap(Type *, std::index_sequence) noexcept { + return [](const void *payload, Args... args) -> Ret { + [[maybe_unused]] const auto arguments = std::forward_as_tuple(std::forward(args)...); + Type *curr = static_cast(const_cast *>(payload)); + + if constexpr(std::is_invocable_r_v>...>) { + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } else { + constexpr auto offset = sizeof...(Args) - sizeof...(Index); + return static_cast(std::invoke(Candidate, curr, std::forward>>(std::get(arguments))...)); + } + }; + } + +public: + /*! @brief Function type of the contained target. */ + using function_type = Ret(const void *, Args...); + /*! @brief Function type of the delegate. */ + using type = Ret(Args...); + /*! @brief Return type of the delegate. */ + using result_type = Ret; + + /*! @brief Default constructor. */ + delegate() noexcept + : instance{nullptr}, + fn{nullptr} {} + + /** + * @brief Constructs a delegate with a given object or payload, if any. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance Optional valid object that fits the purpose. + */ + template + delegate(connect_arg_t, Type &&...value_or_instance) noexcept { + connect(std::forward(value_or_instance)...); + } + + /** + * @brief Constructs a delegate and connects an user defined function with + * optional payload. + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + delegate(function_type *function, const void *payload = nullptr) noexcept { + connect(function, payload); + } + + /** + * @brief Connects a free function or an unbound member to a delegate. + * @tparam Candidate Function or member to connect to the delegate. + */ + template + void connect() noexcept { + instance = nullptr; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *, Args... args) -> Ret { + return Ret(std::invoke(Candidate, std::forward(args)...)); + }; + } else if constexpr(std::is_member_pointer_v) { + fn = wrap(internal::index_sequence_for>>(internal::function_pointer_t{})); + } else { + fn = wrap(internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of the instance overcomes + * the one of the delegate.
+ * When used to connect a free function with payload, its signature must be + * such that the instance is the first argument before the ones used to + * define the delegate itself. + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid reference that fits the purpose. + */ + template + void connect(Type &value_or_instance) noexcept { + instance = &value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, *curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects a free function with payload or a bound member to a + * delegate. + * + * @sa connect(Type &) + * + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid pointer that fits the purpose. + */ + template + void connect(Type *value_or_instance) noexcept { + instance = value_or_instance; + + if constexpr(std::is_invocable_r_v) { + fn = [](const void *payload, Args... args) -> Ret { + Type *curr = static_cast(const_cast *>(payload)); + return Ret(std::invoke(Candidate, curr, std::forward(args)...)); + }; + } else { + fn = wrap(value_or_instance, internal::index_sequence_for(internal::function_pointer_t{})); + } + } + + /** + * @brief Connects an user defined function with optional payload to a + * delegate. + * + * The delegate isn't responsible for the connected object or the payload. + * Users must always guarantee that the lifetime of an instance overcomes + * the one of the delegate.
+ * The payload is returned as the first argument to the target function in + * all cases. + * + * @param function Function to connect to the delegate. + * @param payload User defined arbitrary data. + */ + void connect(function_type *function, const void *payload = nullptr) noexcept { + ENTT_ASSERT(function != nullptr, "Uninitialized function pointer"); + instance = payload; + fn = function; + } + + /** + * @brief Resets a delegate. + * + * After a reset, a delegate cannot be invoked anymore. + */ + void reset() noexcept { + instance = nullptr; + fn = nullptr; + } + + /** + * @brief Returns a pointer to the stored callable function target, if any. + * @return An opaque pointer to the stored callable function target. + */ + [[nodiscard]] function_type *target() const noexcept { + return fn; + } + + /** + * @brief Returns the instance or the payload linked to a delegate, if any. + * @return An opaque pointer to the underlying data. + */ + [[nodiscard]] const void *data() const noexcept { + return instance; + } + + /** + * @brief Triggers a delegate. + * + * The delegate invokes the underlying function and returns the result. + * + * @warning + * Attempting to trigger an invalid delegate results in undefined + * behavior. + * + * @param args Arguments to use to invoke the underlying function. + * @return The value returned by the underlying function. + */ + Ret operator()(Args... args) const { + ENTT_ASSERT(static_cast(*this), "Uninitialized delegate"); + return fn(instance, std::forward(args)...); + } + + /** + * @brief Checks whether a delegate actually stores a listener. + * @return False if the delegate is empty, true otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + // no need to also test instance + return !(fn == nullptr); + } + + /** + * @brief Compares the contents of two delegates. + * @param other Delegate with which to compare. + * @return False if the two contents differ, true otherwise. + */ + [[nodiscard]] bool operator==(const delegate &other) const noexcept { + return fn == other.fn && instance == other.instance; + } + +private: + const void *instance; + function_type *fn; +}; + +/** + * @brief Compares the contents of two delegates. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @param lhs A valid delegate object. + * @param rhs A valid delegate object. + * @return True if the two contents differ, false otherwise. + */ +template +[[nodiscard]] bool operator!=(const delegate &lhs, const delegate &rhs) noexcept { + return !(lhs == rhs); +} + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + */ +template +delegate(connect_arg_t) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Candidate Function or member to connect to the delegate. + * @tparam Type Type of class or type of payload. + */ +template +delegate(connect_arg_t, Type &&) -> delegate>>; + +/** + * @brief Deduction guide. + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + */ +template +delegate(Ret (*)(const void *, Args...), const void * = nullptr) -> delegate; + +} // namespace entt + +#endif + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const noexcept { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + 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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the 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 */ +namespace internal { + +struct basic_dispatcher_handler { + virtual ~basic_dispatcher_handler() = default; + virtual void publish() = 0; + virtual void disconnect(void *) = 0; + virtual void clear() noexcept = 0; + virtual std::size_t size() const noexcept = 0; +}; + +template +class dispatcher_handler final: public basic_dispatcher_handler { + static_assert(std::is_same_v>, "Invalid type"); + + using alloc_traits = std::allocator_traits; + using signal_type = sigh; + using container_type = std::vector>; + +public: + using allocator_type = Allocator; + + dispatcher_handler(const allocator_type &allocator) + : signal{allocator}, + events{allocator} {} + + void publish() override { + const auto length = events.size(); + + for(std::size_t pos{}; pos < length; ++pos) { + signal.publish(events[pos]); + } + + events.erase(events.cbegin(), events.cbegin() + length); + } + + void disconnect(void *instance) override { + bucket().disconnect(instance); + } + + void clear() noexcept override { + events.clear(); + } + + [[nodiscard]] auto bucket() noexcept { + return typename signal_type::sink_type{signal}; + } + + void trigger(Type event) { + signal.publish(event); + } + + template + void enqueue(Args &&...args) { + if constexpr(std::is_aggregate_v && (sizeof...(Args) != 0u || !std::is_default_constructible_v)) { + events.push_back(Type{std::forward(args)...}); + } else { + events.emplace_back(std::forward(args)...); + } + } + + [[nodiscard]] std::size_t size() const noexcept override { + return events.size(); + } + +private: + signal_type signal; + container_type events; +}; + +} // namespace internal +/*! @endcond */ + +/** + * @brief Basic dispatcher implementation. + * + * A dispatcher can be used either to trigger an immediate event or to enqueue + * events to be published all together once per tick.
+ * Listeners are provided in the form of member functions. For each event of + * type `Type`, listeners are such that they can be invoked with an argument of + * type `Type &`, no matter what the return type is. + * + * The dispatcher creates instances of the `sigh` class internally. Refer to the + * documentation of the latter for more details. + * + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class basic_dispatcher { + template + using handler_type = internal::dispatcher_handler; + + using key_type = id_type; + // std::shared_ptr because of its type erased allocator which is useful here + using mapped_type = std::shared_ptr; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + + template + [[nodiscard]] handler_type &assure(const id_type id) { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + auto &&ptr = pools.first()[id]; + + if(!ptr) { + const auto &allocator = get_allocator(); + ptr = std::allocate_shared>(allocator, allocator); + } + + return static_cast &>(*ptr); + } + + template + [[nodiscard]] const handler_type *assure(const id_type id) const { + static_assert(std::is_same_v>, "Non-decayed types not allowed"); + + if(auto it = pools.first().find(id); it != pools.first().cend()) { + return static_cast *>(it->second.get()); + } + + return nullptr; + } + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + basic_dispatcher() + : basic_dispatcher{allocator_type{}} {} + + /** + * @brief Constructs a dispatcher with a given allocator. + * @param allocator The allocator to use. + */ + explicit basic_dispatcher(const allocator_type &allocator) + : pools{allocator, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + basic_dispatcher(basic_dispatcher &&other) noexcept + : pools{std::move(other.pools)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + basic_dispatcher(basic_dispatcher &&other, const allocator_type &allocator) noexcept + : pools{container_type{std::move(other.pools.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + basic_dispatcher &operator=(basic_dispatcher &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || pools.second() == other.pools.second(), "Copying a dispatcher is not allowed"); + pools = std::move(other.pools); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given dispatcher. + * @param other Dispatcher to exchange the content with. + */ + void swap(basic_dispatcher &other) { + using std::swap; + swap(pools, other.pools); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return pools.second(); + } + + /** + * @brief Returns the number of pending events for a given type. + * @tparam Type Type of event for which to return the count. + * @param id Name used to map the event queue within the dispatcher. + * @return The number of pending events for the given type. + */ + template + size_type size(const id_type id = type_hash::value()) const noexcept { + const auto *cpool = assure>(id); + return cpool ? cpool->size() : 0u; + } + + /** + * @brief Returns the total number of pending events. + * @return The total number of pending events. + */ + size_type size() const noexcept { + size_type count{}; + + for(auto &&cpool: pools.first()) { + count += cpool.second->size(); + } + + return count; + } + + /** + * @brief Returns a sink object for the given event and queue. + * + * A sink is an opaque object used to connect listeners to events. + * + * The function type for a listener is _compatible_ with: + * + * @code{.cpp} + * void(Type &); + * @endcode + * + * The order of invocation of the listeners isn't guaranteed. + * + * @sa sink + * + * @tparam Type Type of event of which to get the sink. + * @param id Name used to map the event queue within the dispatcher. + * @return A temporary sink object. + */ + template + [[nodiscard]] auto sink(const id_type id = type_hash::value()) { + return assure(id).bucket(); + } + + /** + * @brief Triggers an immediate event of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void trigger(Type &&value = {}) { + trigger(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Triggers an immediate event on a queue of a given type. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void trigger(const id_type id, Type &&value = {}) { + assure>(id).trigger(std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param args Arguments to use to construct the event. + */ + template + void enqueue(Args &&...args) { + enqueue_hint(type_hash::value(), std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param value An instance of the given type of event. + */ + template + void enqueue(Type &&value) { + enqueue_hint(type_hash>::value(), std::forward(value)); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @tparam Args Types of arguments to use to construct the event. + * @param id Name used to map the event queue within the dispatcher. + * @param args Arguments to use to construct the event. + */ + template + void enqueue_hint(const id_type id, Args &&...args) { + assure(id).enqueue(std::forward(args)...); + } + + /** + * @brief Enqueues an event of the given type. + * @tparam Type Type of event to enqueue. + * @param id Name used to map the event queue within the dispatcher. + * @param value An instance of the given type of event. + */ + template + void enqueue_hint(const id_type id, Type &&value) { + assure>(id).enqueue(std::forward(value)); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type &value_or_instance) { + disconnect(&value_or_instance); + } + + /** + * @brief Utility function to disconnect everything related to a given value + * or instance from a dispatcher. + * @tparam Type Type of class or type of payload. + * @param value_or_instance A valid object that fits the purpose. + */ + template + void disconnect(Type *value_or_instance) { + for(auto &&cpool: pools.first()) { + cpool.second->disconnect(value_or_instance); + } + } + + /** + * @brief Discards all the events stored so far in a given queue. + * @tparam Type Type of event to discard. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void clear(const id_type id = type_hash::value()) { + assure(id).clear(); + } + + /*! @brief Discards all the events queued so far. */ + void clear() noexcept { + for(auto &&cpool: pools.first()) { + cpool.second->clear(); + } + } + + /** + * @brief Delivers all the pending events of a given queue. + * @tparam Type Type of event to send. + * @param id Name used to map the event queue within the dispatcher. + */ + template + void update(const id_type id = type_hash::value()) { + assure(id).publish(); + } + + /*! @brief Delivers all the pending events. */ + void update() const { + for(auto &&cpool: pools.first()) { + cpool.second->publish(); + } + } + +private: + compressed_pair pools; +}; + +} // namespace entt + +#endif + +// #include "signal/emitter.hpp" +#ifndef ENTT_SIGNAL_EMITTER_HPP +#define ENTT_SIGNAL_EMITTER_HPP + +#include +#include +#include +// #include "../container/dense_map.hpp" + +// #include "../core/compressed_pair.hpp" + +// #include "../core/fwd.hpp" + +// #include "../core/type_info.hpp" + +// #include "../core/utility.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief General purpose event emitter. + * + * To create an emitter type, derived classes must inherit from the base as: + * + * @code{.cpp} + * struct my_emitter: emitter { + * // ... + * } + * @endcode + * + * Handlers for the different events are created internally on the fly. It's not + * required to specify in advance the full list of accepted events.
+ * Moreover, whenever an event is published, an emitter also passes a reference + * to itself to its listeners. + * + * @tparam Derived Emitter type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class emitter { + using key_type = id_type; + using mapped_type = std::function; + + using alloc_traits = std::allocator_traits; + using container_allocator = typename alloc_traits::template rebind_alloc>; + using container_type = dense_map, container_allocator>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + + /*! @brief Default constructor. */ + emitter() + : emitter{allocator_type{}} {} + + /** + * @brief Constructs an emitter with a given allocator. + * @param allocator The allocator to use. + */ + explicit emitter(const allocator_type &allocator) + : handlers{allocator, allocator} {} + + /*! @brief Default destructor. */ + virtual ~emitter() noexcept { + static_assert(std::is_base_of_v, Derived>, "Invalid emitter type"); + } + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + emitter(emitter &&other) noexcept + : handlers{std::move(other.handlers)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + emitter(emitter &&other, const allocator_type &allocator) noexcept + : handlers{container_type{std::move(other.handlers.first()), allocator}, allocator} { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This dispatcher. + */ + emitter &operator=(emitter &&other) noexcept { + ENTT_ASSERT(alloc_traits::is_always_equal::value || handlers.second() == other.handlers.second(), "Copying an emitter is not allowed"); + + handlers = std::move(other.handlers); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given emitter. + * @param other Emitter to exchange the content with. + */ + void swap(emitter &other) { + using std::swap; + swap(handlers, other.handlers); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return handlers.second(); + } + + /** + * @brief Publishes a given event. + * @tparam Type Type of event to trigger. + * @param value An instance of the given type of event. + */ + template + void publish(Type &&value) { + if(const auto id = type_id().hash(); handlers.first().contains(id)) { + handlers.first()[id](&value); + } + } + + /** + * @brief Registers a listener with the event emitter. + * @tparam Type Type of event to which to connect the listener. + * @param func The listener to register. + */ + template + void on(std::function func) { + handlers.first().insert_or_assign(type_id().hash(), [func = std::move(func), this](void *value) { + func(*static_cast(value), static_cast(*this)); + }); + } + + /** + * @brief Disconnects a listener from the event emitter. + * @tparam Type Type of event of the listener. + */ + template + void erase() { + handlers.first().erase(type_hash>>::value()); + } + + /*! @brief Disconnects all the listeners. */ + void clear() noexcept { + handlers.first().clear(); + } + + /** + * @brief Checks if there are listeners registered for the specific event. + * @tparam Type Type of event to test. + * @return True if there are no listeners registered, false otherwise. + */ + template + [[nodiscard]] bool contains() const { + return handlers.first().contains(type_hash>>::value()); + } + + /** + * @brief Checks if there are listeners registered with the event emitter. + * @return True if there are no listeners registered, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return handlers.first().empty(); + } + +private: + compressed_pair handlers; +}; + +} // namespace entt + +#endif + +// #include "signal/sigh.hpp" +#ifndef ENTT_SIGNAL_SIGH_HPP +#define ENTT_SIGNAL_SIGH_HPP + +#include +#include +#include +#include +#include +// #include "delegate.hpp" + +// #include "fwd.hpp" + + +namespace entt { + +/** + * @brief Sink class. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid signal handler type. + */ +template +class sink; + +/** + * @brief Unmanaged signal handler. + * + * Primary template isn't defined on purpose. All the specializations give a + * compile-time error unless the template parameter is a function type. + * + * @tparam Type A valid function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh; + +/** + * @brief Unmanaged signal handler. + * + * It works directly with references to classes and pointers to member functions + * as well as pointers to free functions. Users of this class are in charge of + * disconnecting instances before deleting them. + * + * This class serves mainly two purposes: + * + * * Creating signals to use later to notify a bunch of listeners. + * * Collecting results from a set of functions like in a voting system. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sigh { + friend class sink>; + + using alloc_traits = std::allocator_traits; + using delegate_type = delegate; + using container_type = std::vector>; + +public: + /*! @brief Allocator type. */ + using allocator_type = Allocator; + /*! @brief Unsigned integer type. */ + using size_type = std::size_t; + /*! @brief Sink type. */ + using sink_type = sink>; + + /*! @brief Default constructor. */ + sigh() noexcept(std::is_nothrow_default_constructible_v &&std::is_nothrow_constructible_v) + : sigh{allocator_type{}} {} + + /** + * @brief Constructs a signal handler with a given allocator. + * @param allocator The allocator to use. + */ + explicit sigh(const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{allocator} {} + + /** + * @brief Copy constructor. + * @param other The instance to copy from. + */ + sigh(const sigh &other) noexcept(std::is_nothrow_copy_constructible_v) + : calls{other.calls} {} + + /** + * @brief Allocator-extended copy constructor. + * @param other The instance to copy from. + * @param allocator The allocator to use. + */ + sigh(const sigh &other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{other.calls, allocator} {} + + /** + * @brief Move constructor. + * @param other The instance to move from. + */ + sigh(sigh &&other) noexcept(std::is_nothrow_move_constructible_v) + : calls{std::move(other.calls)} {} + + /** + * @brief Allocator-extended move constructor. + * @param other The instance to move from. + * @param allocator The allocator to use. + */ + sigh(sigh &&other, const allocator_type &allocator) noexcept(std::is_nothrow_constructible_v) + : calls{std::move(other.calls), allocator} {} + + /** + * @brief Copy assignment operator. + * @param other The instance to copy from. + * @return This signal handler. + */ + sigh &operator=(const sigh &other) noexcept(std::is_nothrow_copy_assignable_v) { + calls = other.calls; + return *this; + } + + /** + * @brief Move assignment operator. + * @param other The instance to move from. + * @return This signal handler. + */ + sigh &operator=(sigh &&other) noexcept(std::is_nothrow_move_assignable_v) { + calls = std::move(other.calls); + return *this; + } + + /** + * @brief Exchanges the contents with those of a given signal handler. + * @param other Signal handler to exchange the content with. + */ + void swap(sigh &other) noexcept(std::is_nothrow_swappable_v) { + using std::swap; + swap(calls, other.calls); + } + + /** + * @brief Returns the associated allocator. + * @return The associated allocator. + */ + [[nodiscard]] constexpr allocator_type get_allocator() const noexcept { + return calls.get_allocator(); + } + + /** + * @brief Number of listeners connected to the signal. + * @return Number of listeners currently connected. + */ + [[nodiscard]] size_type size() const noexcept { + return calls.size(); + } + + /** + * @brief Returns false if at least a listener is connected to the signal. + * @return True if the signal has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return calls.empty(); + } + + /** + * @brief Triggers a signal. + * + * All the listeners are notified. Order isn't guaranteed. + * + * @param args Arguments to use to invoke listeners. + */ + void publish(Args... args) const { + for(auto pos = calls.size(); pos; --pos) { + calls[pos - 1u](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 pos = calls.size(); pos; --pos) { + if constexpr(std::is_void_v || !std::is_invocable_v) { + calls[pos - 1u](args...); + + if constexpr(std::is_invocable_r_v) { + if(func()) { + break; + } + } else { + func(); + } + } else { + if constexpr(std::is_invocable_r_v) { + if(func(calls[pos - 1u](args...))) { + break; + } + } else { + func(calls[pos - 1u](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 { + template + friend class sink; + + connection(delegate fn, void *ref) + : disconnect{fn}, signal{ref} {} + +public: + /*! @brief Default constructor. */ + connection() + : disconnect{}, + signal{} {} + + /** + * @brief Checks whether a connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(disconnect); + } + + /*! @brief Breaks the connection. */ + void release() { + if(disconnect) { + disconnect(signal); + disconnect.reset(); + } + } + +private: + delegate disconnect; + void *signal; +}; + +/** + * @brief Scoped connection class. + * + * Opaque object the aim of which is to allow users to release an already + * estabilished connection without having to keep a reference to the signal or + * the sink that generated it.
+ * A scoped connection automatically breaks the link between the two objects + * when it goes out of scope. + */ +struct scoped_connection { + /*! @brief Default constructor. */ + scoped_connection() = default; + + /** + * @brief Constructs a scoped connection from a basic connection. + * @param other A valid connection object. + */ + scoped_connection(const connection &other) + : conn{other} {} + + /*! @brief Default copy constructor, deleted on purpose. */ + scoped_connection(const scoped_connection &) = delete; + + /** + * @brief Move constructor. + * @param other The scoped connection to move from. + */ + scoped_connection(scoped_connection &&other) noexcept + : conn{std::exchange(other.conn, {})} {} + + /*! @brief Automatically breaks the link on destruction. */ + ~scoped_connection() { + conn.release(); + } + + /** + * @brief Default copy assignment operator, deleted on purpose. + * @return This scoped connection. + */ + scoped_connection &operator=(const scoped_connection &) = delete; + + /** + * @brief Move assignment operator. + * @param other The scoped connection to move from. + * @return This scoped connection. + */ + scoped_connection &operator=(scoped_connection &&other) noexcept { + conn = std::exchange(other.conn, {}); + return *this; + } + + /** + * @brief Acquires a connection. + * @param other The connection object to acquire. + * @return This scoped connection. + */ + scoped_connection &operator=(connection other) { + conn = std::move(other); + return *this; + } + + /** + * @brief Checks whether a scoped connection is properly initialized. + * @return True if the connection is properly initialized, false otherwise. + */ + [[nodiscard]] explicit operator bool() const noexcept { + return static_cast(conn); + } + + /*! @brief Breaks the connection. */ + void release() { + conn.release(); + } + +private: + connection conn; +}; + +/** + * @brief Sink class. + * + * A sink is used to connect listeners to signals and to disconnect them.
+ * The function type for a listener is the one of the signal to which it + * belongs. + * + * The clear separation between a signal and a sink permits to store the former + * as private data member without exposing the publish functionality to the + * users of the class. + * + * @warning + * Lifetime of a sink must not overcome that of the signal to which it refers. + * In any other case, attempting to use a sink results in undefined behavior. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +class sink> { + using signal_type = sigh; + using delegate_type = typename signal_type::delegate_type; + 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(); + } + + template + void disconnect_if(Func callback) { + for(auto pos = signal->calls.size(); pos; --pos) { + if(auto &elem = signal->calls[pos - 1u]; callback(elem)) { + elem = std::move(signal->calls.back()); + signal->calls.pop_back(); + } + } + } + +public: + /** + * @brief Constructs a sink that is allowed to modify a given signal. + * @param ref A valid reference to a signal object. + */ + sink(sigh &ref) noexcept + : signal{&ref} {} + + /** + * @brief Returns false if at least a listener is connected to the sink. + * @return True if the sink has no listeners connected, false otherwise. + */ + [[nodiscard]] bool empty() const noexcept { + return signal->calls.empty(); + } + + /** + * @brief Connects a free function (with or without payload), a bound or an + * unbound member to a signal. + * @tparam Candidate Function or member to connect to the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + * @return A properly initialized connection object. + */ + template + connection connect(Type &&...value_or_instance) { + disconnect(value_or_instance...); + + delegate_type call{}; + call.template connect(value_or_instance...); + signal->calls.push_back(std::move(call)); + + delegate conn{}; + conn.template connect<&release>(value_or_instance...); + return {std::move(conn), signal}; + } + + /** + * @brief Disconnects a free function (with or without payload), a bound or + * an unbound member from a signal. + * @tparam Candidate Function or member to disconnect from the signal. + * @tparam Type Type of class or type of payload, if any. + * @param value_or_instance A valid object that fits the purpose, if any. + */ + template + void disconnect(Type &&...value_or_instance) { + delegate_type call{}; + call.template connect(value_or_instance...); + disconnect_if([&call](const auto &elem) { return elem == call; }); + } + + /** + * @brief Disconnects free functions with payload or bound members from a + * signal. + * @param value_or_instance A valid object that fits the purpose. + */ + void disconnect(const void *value_or_instance) { + if(value_or_instance) { + disconnect_if([value_or_instance](const auto &elem) { return elem.data() == value_or_instance; }); + } + } + + /*! @brief Disconnects all the listeners from a signal. */ + void disconnect() { + signal->calls.clear(); + } + +private: + signal_type *signal; +}; + +/** + * @brief Deduction guide. + * + * It allows to deduce the signal handler type of a sink directly from the + * signal it refers to. + * + * @tparam Ret Return type of a function type. + * @tparam Args Types of arguments of a function type. + * @tparam Allocator Type of allocator used to manage memory and elements. + */ +template +sink(sigh &) -> sink>; + +} // namespace entt + +#endif + +// IWYU pragma: end_exports diff --git a/dart/v7/comps/all.hpp b/dart/v7/comps/all.hpp new file mode 100644 index 0000000000000..b641c1335066f --- /dev/null +++ b/dart/v7/comps/all.hpp @@ -0,0 +1,52 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace dart::v7::comps { + +struct SkeletonTag +{ +}; + +struct RigidBodyTag +{ +}; + +struct Name +{ + std::string data; +}; + +} // namespace dart::v7::comps diff --git a/dart/v7/ecs/context.cpp b/dart/v7/ecs/context.cpp new file mode 100644 index 0000000000000..61e39185687b2 --- /dev/null +++ b/dart/v7/ecs/context.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/ecs/context.hpp" + +namespace dart::v7 { + +Context::Context() +{ + static size_t globalId = 0; + mId = globalId++; +} + +size_t Context::getId() const +{ + return mId; +} + +} // namespace dart::v7 diff --git a/dart/v7/ecs/context.hpp b/dart/v7/ecs/context.hpp new file mode 100644 index 0000000000000..2d909ecee7948 --- /dev/null +++ b/dart/v7/ecs/context.hpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace dart::v7 { + +class Context : public entt::registry +{ +public: + Context(); + + size_t getId() const; + +private: + size_t mId; +}; + +} // namespace dart::v7 diff --git a/dart/v7/ecs/entity.hpp b/dart/v7/ecs/entity.hpp new file mode 100644 index 0000000000000..b7e4b0c456001 --- /dev/null +++ b/dart/v7/ecs/entity.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace dart::v7 { + +using Entity = entt::entity; + +} // namespace dart::v7 \ No newline at end of file diff --git a/dart/v7/ecs/include_entt.hpp b/dart/v7/ecs/include_entt.hpp new file mode 100644 index 0000000000000..0eec40abc321f --- /dev/null +++ b/dart/v7/ecs/include_entt.hpp @@ -0,0 +1,41 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#if DART_USE_SYSTEM_ENTT + #include +#else + #include +#endif diff --git a/dart/v7/ecs/system.cpp b/dart/v7/ecs/system.cpp new file mode 100644 index 0000000000000..5bacfd8b5aac2 --- /dev/null +++ b/dart/v7/ecs/system.cpp @@ -0,0 +1,39 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/ecs/system.hpp" + +namespace dart::v7 { + +// + +} // namespace dart::v7 diff --git a/dart/v7/ecs/system.hpp b/dart/v7/ecs/system.hpp new file mode 100644 index 0000000000000..984844fb56d3f --- /dev/null +++ b/dart/v7/ecs/system.hpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace dart::v7 { + +class System +{ +public: + System() = default; + virtual ~System() = default; +}; + +} // namespace dart::v7 diff --git a/dart/v7/ecs/system_graph.cpp b/dart/v7/ecs/system_graph.cpp new file mode 100644 index 0000000000000..5bd202b261be3 --- /dev/null +++ b/dart/v7/ecs/system_graph.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/ecs/system_graph.hpp" + +namespace dart::v7 { + +void SystemGraph::run() +{ + // +} + +} // namespace dart::v7 diff --git a/dart/v7/ecs/system_graph.hpp b/dart/v7/ecs/system_graph.hpp new file mode 100644 index 0000000000000..feba6afd84116 --- /dev/null +++ b/dart/v7/ecs/system_graph.hpp @@ -0,0 +1,45 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace dart::v7 { + +class SystemGraph +{ +public: + void run(); +}; + +} // namespace dart::v7 diff --git a/dart/v7/rigid_body.cpp b/dart/v7/rigid_body.cpp new file mode 100644 index 0000000000000..256d8fcaecb4d --- /dev/null +++ b/dart/v7/rigid_body.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/rigid_body.hpp" + +#include "dart/v7/comps/all.hpp" +#include "dart/v7/ecs/context.hpp" +#include "dart/v7/world.hpp" + +namespace dart::v7 { + +RigidBody RigidBody::Create( + Context& context, World* world, const RigidBodyConfig& config) +{ + Entity e = context.create(); + context.emplace(e); + + auto& nameComp = context.emplace(e); + nameComp.data = config.name; + + return RigidBody(world, e); +} + +void RigidBody::Remove(Context& context, Entity e) +{ + context.destroy(e); +} + +RigidBody::RigidBody(World* world, Entity e) : WorldObject(world, e) +{ + // Empty +} + +bool RigidBody::isValid() const +{ + const auto& context = mWorld->getContext(); + if (!context.all_of(mEntity)) { + return false; + } + return true; +} + +const std::string& RigidBody::getName() const +{ + const auto& context = getContext(); + return context.get(mEntity).data; +} + +void RigidBody::setName(const std::string& name) +{ + auto& context = getMutableContext(); + context.get(mEntity).data = name; +} + +} // namespace dart::v7 diff --git a/dart/v7/rigid_body.hpp b/dart/v7/rigid_body.hpp new file mode 100644 index 0000000000000..e55669d827c29 --- /dev/null +++ b/dart/v7/rigid_body.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include +#include + +namespace dart::v7 { + +struct RigidBodyConfig +{ + std::string name; + double mass; +}; + +class RigidBody : public WorldObject +{ +public: + static RigidBody Create( + Context& context, World* world, const RigidBodyConfig& config); + + static void Remove(Context& context, Entity e); + + explicit RigidBody(World* world, Entity e); + + bool isValid() const override; + + const std::string& getName() const; + void setName(const std::string& name); +}; + +} // namespace dart::v7 diff --git a/dart/v7/world.cpp b/dart/v7/world.cpp new file mode 100644 index 0000000000000..957e290570f99 --- /dev/null +++ b/dart/v7/world.cpp @@ -0,0 +1,131 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/world.hpp" + +#include "dart/v7/ecs/system_graph.hpp" +#include "dart/v7/logging.hpp" + +namespace dart::v7 { + +struct World::Private +{ + std::unique_ptr context; + SystemGraph graph; + Entity entity; +}; + +World::World() : data(std::make_unique()) +{ + data->context = std::make_unique(); + data->entity = data->context->create(); +} + +World::~World() +{ + // Empty +} + +const Context& World::getContext() const +{ + return *data->context; +} + +Context& World::getMutableContext() +{ + return *data->context; +} + +bool World::save(WorldSnapshot& data) const +{ + DART_FATAL("Not implemented yet"); + (void)data; + return true; +} + +bool World::saveToFile(const std::filesystem::path& path) const +{ + DART_FATAL("Not implemented yet"); + (void)path; + return true; +} + +bool World::load(const WorldSnapshot& data) +{ + DART_FATAL("Not implemented yet"); + (void)data; + return true; +} + +bool World::loadFromFile(const std::filesystem::path& path) +{ + DART_FATAL("Not implemented yet"); + (void)path; + return true; +} + +void World::step() +{ + DART_FATAL("Not implemented yet"); + data->graph.run(); +} + +RigidBody World::addRigidBody(const RigidBodyConfig& config) +{ + return RigidBody::Create(*data->context, this, config); +} + +bool World::hasRigidBody(const RigidBody& rigidBody) const +{ + Context& context = *data->context; + + if (context.getId() != rigidBody.getContext().getId()) { + return false; + } + + if (!context.valid(rigidBody.getEntity())) { + return false; + } + + if (!rigidBody.isValid()) { + return false; + } + + return true; +} + +void World::removeRigidBody(const RigidBody& rigidBody) +{ + RigidBody::Remove(*data->context, rigidBody.getEntity()); +} + +} // namespace dart::v7 diff --git a/dart/v7/world.hpp b/dart/v7/world.hpp new file mode 100644 index 0000000000000..985302c57e3c7 --- /dev/null +++ b/dart/v7/world.hpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include + +#include +#include + +#include +#include + +namespace dart::v7 { + +struct SceneDesc +{ + // +}; + +struct WorldConfig +{ + double stepTime{0.001}; +}; + +struct WorldSnapshot +{ + bool valid{false}; + Context context; +}; + +struct StepInfo +{ + double timeS; +}; + +class World +{ +public: + World(); + ~World(); + + const Context& getContext() const; + Context& getMutableContext(); + + bool save(WorldSnapshot& data) const; + bool saveToFile(const std::filesystem::path& path) const; + + bool load(const WorldSnapshot& data); + bool loadFromFile(const std::filesystem::path& path); + + void step(); + + RigidBody addRigidBody(const RigidBodyConfig& config = RigidBodyConfig()); + bool hasRigidBody(const RigidBody& rigidBody) const; + void removeRigidBody(const RigidBody& rigidBody); + +private: + struct Private; + std::unique_ptr data; +}; + +} // namespace dart::v7 diff --git a/dart/v7/world_object.cpp b/dart/v7/world_object.cpp new file mode 100644 index 0000000000000..92704951a9971 --- /dev/null +++ b/dart/v7/world_object.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/v7/world_object.hpp" + +#include "dart/v7/assert.hpp" +#include "dart/v7/world.hpp" + +namespace dart::v7 { + +WorldObject::WorldObject(World* world, Entity e) : mWorld(world), mEntity(e) +{ + DART_ASSERT(world); +} + +const World& WorldObject::getWorld() const +{ + return *mWorld; +} + +Entity WorldObject::getEntity() const +{ + return mEntity; +} + +const Context& WorldObject::getContext() const +{ + return mWorld->getContext(); +} + +Context& WorldObject::getMutableContext() +{ + return mWorld->getMutableContext(); +} + +} // namespace dart::v7 diff --git a/dart/v7/world_object.hpp b/dart/v7/world_object.hpp new file mode 100644 index 0000000000000..f236ad4cd9f34 --- /dev/null +++ b/dart/v7/world_object.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +#include + +namespace dart::v7 { + +class WorldObject +{ +public: + explicit WorldObject(World* world, Entity e); + + const World& getWorld() const; + Entity getEntity() const; + + const Context& getContext() const; + Context& getMutableContext(); + + virtual bool isValid() const = 0; + +protected: + World* mWorld; + Entity mEntity; + +private: + friend class World; +}; + +} // namespace dart::v7 diff --git a/docs/readthedocs/developer_guide/build.rst b/docs/readthedocs/developer_guide/build.rst index 6d0fa93570c46..d77c1f9357d3e 100644 --- a/docs/readthedocs/developer_guide/build.rst +++ b/docs/readthedocs/developer_guide/build.rst @@ -95,14 +95,14 @@ manager. The following command will install the required dependencies: .. code-block:: bash - $ vcpkg install --triplet x64-windows assimp eigen3 fcl fmt spdlog + $ vcpkg install --triplet x64-windows assimp eigen3 entt fcl fmt spdlog The following command will install the optional dependencies: .. code-block:: bash $ vcpkg install --triplet x64-windows \ - assimp eigen3 fcl fmt spdlog bullet3 freeglut glfw3 nlopt ode \ + assimp eigen3 entt fcl fmt spdlog bullet3 freeglut glfw3 nlopt ode \ opencl opengl osg pagmo2 pybind11 tinyxml2 urdfdom yaml-cpp Arch Linux (experimental) diff --git a/setup.py b/setup.py index e24332b0bd9c7..dedee96f9748a 100644 --- a/setup.py +++ b/setup.py @@ -104,6 +104,8 @@ def build_extension(self, ext: CMakeExtension) -> None: except ImportError: pass + cmake_args += ["-DDART_USE_SYSTEM_ENTT=OFF"] + else: # Single config generators are handled "normally" single_config = any(x in cmake_generator for x in {"NMake", "Ninja"}) @@ -127,6 +129,8 @@ def build_extension(self, ext: CMakeExtension) -> None: ] build_args += ["--config", cfg] + cmake_args += ["-DDART_USE_SYSTEM_ENTT=ON"] + if sys.platform.startswith("darwin"): # Enable cross-compilation support for macOS and respect ARCHFLAGS if set archs = re.findall(r"-arch (\S+)", os.environ.get("ARCHFLAGS", "")) diff --git a/tests/unit/common/test_ecs.cpp b/tests/unit/common/test_ecs.cpp new file mode 100644 index 0000000000000..7cebe5636c17d --- /dev/null +++ b/tests/unit/common/test_ecs.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2011-2024, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +namespace { + +struct position +{ + float x; + float y; +}; + +struct velocity +{ + float dx; + float dy; +}; + +void update(entt::registry& registry) +{ + auto view = registry.view(); + + // use a callback + view.each([](const auto& /*pos*/, auto& /*vel*/) { /* ... */ }); + + // use an extended callback + view.each([](const auto /*entity*/, + const auto& /*pos*/, + auto& /*vel*/) { /* ... */ }); + + // use a range-for + for (auto [entity, pos, vel] : view.each()) { + (void)entity; + (void)pos; + (void)vel; + // ... + } + + // use forward iterators and get only the components of interest + for (auto entity : view) { + auto& vel = view.get(entity); + (void)vel; + // ... + } +} + +} // namespace + +GTEST_TEST(EcsTest, Basics) +{ + entt::registry registry; + + for (auto i = 0u; i < 10u; ++i) { + const auto entity = registry.create(); + registry.emplace(entity, i * 1.f, i * 1.f); + if (i % 2 == 0) { + registry.emplace(entity, i * .1f, i * .1f); + } + } + + update(registry); +} diff --git a/tests/unit/v7/test_world.cpp b/tests/unit/v7/test_world.cpp new file mode 100644 index 0000000000000..9a68b7b698feb --- /dev/null +++ b/tests/unit/v7/test_world.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/main/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include + +#include + +using namespace dart; +using namespace v7; + +TEST(WorldTest, SaveLoadWorld) +{ + auto w = World(); + w.saveToFile("test_world.urdf"); +} + +TEST(WorldTest, SingleStep) +{ + auto w = World(); + w.step(); +} + +TEST(WorldTest, RigidBodyAddRemove) +{ + auto w = World(); + auto body1 = w.addRigidBody({"body1", 0.0}); + + EXPECT_EQ(body1.getName(), "body1"); + + EXPECT_TRUE(w.hasRigidBody(body1)); + + w.removeRigidBody(body1); + + EXPECT_FALSE(w.hasRigidBody(body1)); + + auto body2 = w.addRigidBody(); + EXPECT_FALSE(w.hasRigidBody(body1)); + EXPECT_TRUE(w.hasRigidBody(body2)); +}